<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Conal Elliott &#187; trie</title>
	<atom:link href="http://conal.net/blog/tag/trie/feed" rel="self" type="application/rss+xml" />
	<link>http://conal.net/blog</link>
	<description>Inspirations &#38; experiments, mainly about denotative/functional programming in Haskell</description>
	<lastBuildDate>Thu, 25 Jul 2019 18:15:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.17</generator>
	<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2F&amp;language=en_US&amp;category=text&amp;title=Conal+Elliott&amp;description=Inspirations+%26amp%3B+experiments%2C+mainly+about+denotative%2Ffunctional+programming+in+Haskell&amp;tags=blog" type="text/html" />
	<item>
		<title>From tries to trees</title>
		<link>http://conal.net/blog/posts/from-tries-to-trees</link>
		<comments>http://conal.net/blog/posts/from-tries-to-trees#comments</comments>
		<pubDate>Tue, 01 Feb 2011 18:36:32 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[number]]></category>
		<category><![CDATA[tree]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[vector]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=314</guid>
		<description><![CDATA[This post is the last of a series of six relating numbers, vectors, and trees, revolving around the themes of static size-typing and memo tries. We&#8217;ve seen that length-typed vectors form a trie for bounded numbers, and can handily represent numbers as well. We&#8217;ve also seen that n-dimensional vectors themselves have an elegant trie, which [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- teaser -->

<p
>This post is the last of a series of six relating numbers, vectors, and trees, revolving around the themes of static size-typing and <a href="http://conal.net/blog/tag/memoization/" title="Posts on memoization"
  >memo</a
  > <a href="http://conal.net/blog/tag/trie/" title="Posts on tries"
  >tries</a
  >. We&#8217;ve seen that length-typed vectors form a trie for bounded numbers, and can handily represent numbers as well. We&#8217;ve also seen that <span class="math"
  ><em
    >n</em
    ></span
  >-dimensional vectors themselves have an elegant trie, which is the <span class="math"
  ><em
    >n</em
    ></span
  >-ary composition of the element type&#8217;s trie functor:</p
>

<pre class="sourceCode haskell"
><code
  ><span class="kw"
    >type</span
    > <span class="dt"
    >VTrie</span
    > n a <span class="fu"
    >=</span
    > <span class="dt"
    >Trie</span
    > a <span class="fu"
    >:^</span
    > n <br
     /></code
  ></pre
>

<p
>where for any functor <code
  >f</code
  > and natural number type <code
  >n</code
  >,</p
>

<pre class="sourceCode haskell"
><code
  >f <span class="fu"
    >:^</span
    > n <span class="dt"
    >&#8773;</span
    > f &#8728; &#8943; &#8728; f  <span class="co"
    >-- (n times)</span
    ><br
     /></code
  ></pre
>

<p
>This final post in the series places this elegant mechanism of <span class="math"
  ><em
    >n</em
    ></span
  >-ary functor composition into a familiar &amp; useful context, namely trees. Again, type-encoded Peano numbers are central. Just as <code
  >BNat</code
  > uses these number types to (statically) bound natural numbers (e.g., for a vector index or a numerical digit), and <code
  >Vec</code
  > uses number types to capture vector <em
  >length</em
  >, we'll next use number types to capture tree <em
  >depth</em
  >.</p
>

<p
><strong
  >Edits:</strong
  ></p
>

<ul
><li
  >2011-02-02: Changes thanks to comments from Sebastian Fischer<ul
    ><li
      >Added note about number representations and leading zeros (without size-typing).</li
      ><li
      >Added pointer to <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization/" title="blog post"
    ><em
      >Memoizing polymorphic functions via unmemoization</em
      ></a
    > for derivation of <code
    >Tree d a &#8773; [d] &#8594; a</code
    >.</li
      ><li
      >Fixed signatures for some <code
    >Branch</code
    > variants, bringing type parameter <code
    >a</code
    > into parens.</li
      ><li
      >Clarification about number of <code
    >VecTree</code
    > vs pairing constructors in remarks on left- vs right-folded trees.</li
      ></ul
    ></li
  ><li
  >2011-02-06: Fixed link to <a href="http://www.eecs.usma.edu/webs/people/okasaki/pubs.html#icfp99" title="Paper by Chris Okasaki"
    ><em
      >From Fast Exponentiation to Square Matrices</em
      ></a
    >.</li
  ></ul
>

<p><span id="more-314"></span></p>

<div id="infinite-trees"
><h3
  >Infinite trees</h3
  ><p
  >In the post <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization/" title="blog post"
    ><em
      >Memoizing polymorphic functions via unmemoization</em
      ></a
    >, I played with a number of container types, looking at which ones are tries over what domain types. I referred to these domain types as &quot;index types&quot; for the container type. One such container was a type of infinite binary trees with values at every node:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >BinTree</span
      > a (<span class="dt"
      >BinTree</span
      > a) (<span class="dt"
      >BinTree</span
      > a)<br
       /></code
    ></pre
  ><p
  >By the usual exponent laws, this <code
    >BinTree</code
    > functor is (isomorphic to) the functor of tries over a type of binary natural numbers formulated as follows:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinNat</span
      > <span class="fu"
      >=</span
      > <span class="dt"
      >Zero</span
      > <span class="fu"
      >|</span
      > <span class="dt"
      >Even</span
      > <span class="dt"
      >BinNat</span
      > <span class="fu"
      >|</span
      > <span class="dt"
      >Odd</span
      > <span class="dt"
      >BinNat</span
      ><br
       /></code
    ></pre
  ><p
  >As a variation on this <code
    >BinTree</code
    >, we can replace the two subtrees with a <em
    >pair</em
    > of subtrees:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >BinTree</span
      > a (<span class="dt"
      >Pair</span
      > (<span class="dt"
      >BinTree</span
      > a))<br
       /></code
    ></pre
  ><p
  >Where <code
    >Pair</code
    > could be defined as</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >Pair</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >Pair</span
      > a a<br
       /></code
    ></pre
  ><p
  >or, using <a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
    >functor combinators</a
    >,</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > <span class="dt"
      >Pair</span
      > <span class="fu"
      >=</span
      > <span class="dt"
      >Id</span
      > &#215; <span class="dt"
      >Id</span
      ><br
       /></code
    ></pre
  ><p
  >The reformulation of <code
    >BinTree</code
    > leads to a slightly different representation for our index type, a little-endian list of bits:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinNat</span
      > <span class="fu"
      >=</span
      > <span class="dt"
      >Zero</span
      > <span class="fu"
      >|</span
      > <span class="dt"
      >NonZero</span
      > <span class="dt"
      >Bool</span
      > <span class="dt"
      >BinNat</span
      ><br
       /></code
    ></pre
  ><p
  >or simply</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > <span class="dt"
      >BinNat</span
      > <span class="fu"
      >=</span
      > [<span class="dt"
      >Bool</span
      >]<br
       /></code
    ></pre
  ><p
  >Note that <code
    >Bool</code
    > is the index type for <code
    >Pair</code
    > (and conversely, <code
    >Pair</code
    > is the trie for <code
    >Bool</code
    >), which suggests that we play this same trick for <em
    >all</em
    > index types and their corresponding trie functors. Generalizing,</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >Tree</span
      > d a <span class="fu"
      >=</span
      > <span class="dt"
      >Tree</span
      > a (d &#8603; <span class="dt"
      >Tree</span
      > d a)<br
       /></code
    ></pre
  ><p
  >where <code
    >k &#8603; v</code
    > is short for <code
    >Trie k v</code
    >, and <code
    >Trie k</code
    > is the trie functor associated with the type <code
    >k</code
    >. See <a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
    ><em
      >Elegant memoization with higher-order types</em
      ></a
    >.</p
  ><p
  >These generalized trees are indexed by little-endian natural numbers over a &quot;digit&quot; type <code
    >d</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >Tree</span
      > d a <span class="dt"
      >&#8773;</span
      > [d] &#8594; a<br
       /></code
    ></pre
  ><p
  >which is to say that <code
    >Tree d</code
    > is a trie for <code
    >[d]</code
    >. See <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization/" title="blog post"
    ><em
      >Memoizing polymorphic functions via unmemoization</em
      ></a
    > for a derivation.</p
  ><p
  >Note that all of these number representations have a serious problem, which is that they distinguish between number representations that differ only by leading zeros. The size-typed versions do not have this problem.</p
  ></div
>

<div id="finite-trees"
><h3
  >Finite trees</h3
  ><p
  >The reason I chose infinite trees was that the finite tree types I knew of have choice-points/alternatives, and so are isomorphic to sums. I don't know of trie-construction techniques that synthesize sums.</p
  ><p
  >Can we design a tree type that is both finite and choice-free? We've already tackled a similar challenge above with lists earlier in previous posts.</p
  ><p
  >In <a href="http://conal.net/blog/posts/fixing-lists/" title="blog post"
    ><em
      >Fixing lists</em
      ></a
    >, I wanted to &quot;fix&quot; lists, in the sense of eliminating the choice points in the standard type <code
    >[a]</code
    > so that the result could be a trie. Doing so led to the type <code
    >Vec n a</code
    >, which appears to have choice points, due to the two constructors <code
    >ZVec</code
    > and <code
    >(:&lt;)</code
    >, but for any given <code
    >n</code
    >, at most one constructor is applicable. (For this reason, regular algebraic data types are inadequate.) For handy review,</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >Vec</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >ZVec</span
      > <span class="dv"
      >&#8759;</span
      >                <span class="dt"
      >Vec</span
      > <span class="dt"
      >Z</span
      >     a<br
       />  (<span class="fu"
      >:&lt;</span
      >) <span class="dv"
      >&#8759;</span
      > a &#8594; <span class="dt"
      >Vec</span
      > n a &#8594; <span class="dt"
      >Vec</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Let's try the same trick with trees, fixing depth instead length, to get depth-typed binary trees:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      > a                         &#8594; <span class="dt"
      >BinTree</span
      > <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >BinTree</span
      > n a &#8594; <span class="dt"
      >BinTree</span
      > n a &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Again, we can replace the two subtrees with a single pair of subtrees in the <code
    >Branch</code
    > constructor::</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Pair</span
      > (<span class="dt"
      >BinTree</span
      > n) a &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Or, recalling that <code
    >Bool</code
    > is the index type for <code
    >Pair</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >                    a &#8594; <span class="dt"
      >BinTree</span
      > <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >Bool</span
      > &#8603; <span class="dt"
      >BinTree</span
      > n a) &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >The use of <code
    >Bool</code
    > is rather ad hoc. Its useful property is isomorphism with <code
    >1 + 1</code
    >, whose corresponding trie functor is <code
    >Id + Id</code
    >, i.e., <code
    >Pair</code
    >. In the post <a href="http://conal.net/blog/posts/type-bounded-numbers/" title="blog post"
    ><em
      >Type-bounded numbers</em
      ></a
    >, we saw another, more systematic, type isomorphic to <code
    >1 + 1</code
    >, which is <code
    >BNat TwoT</code
    > (i.e., <code
    >BNat (S (S Z))</code
    >), which is the type of natural numbers less than two.</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >BNat</span
      > <span class="dt"
      >TwoT</span
      > &#8603; <span class="dt"
      >BinTree</span
      > n a) &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >This replacement suggests a generalization from binary trees to <span class="math"
    ><em
      >b</em
      ></span
    >-ary trees (i.e., having branch factor <span class="math"
    ><em
      >b</em
      ></span
    >).</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >VecTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >                        a &#8594; <span class="dt"
      >VecTree</span
      > b <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >BNat</span
      > b &#8603; <span class="dt"
      >VecTree</span
      > b n a) &#8594; <span class="dt"
      >VecTree</span
      > b (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Recall that the motivation for <code
    >BNat b</code
    > was as an index type for <code
    >Vec b</code
    >, which is to say that <code
    >Vec b</code
    > turned out to be the trie functor for the type <code
    >BNat b</code
    >. With this relationship in mind, the <code
    >Branch</code
    > type is equivalent to</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Vec</span
      > b (<span class="dt"
      >VecTree</span
      > b n a) &#8594; <span class="dt"
      >VecTree</span
      > b (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Unsurprisingly then, a <span class="math"
    ><em
      >b</em
      ></span
    >-ary tree is either a single leaf value or a branch node containing <span class="math"
    ><em
      >b</em
      ></span
    > subtrees. Also, the depth of a leaf is zero, and the depth of a branch node containing <span class="math"
    ><em
      >b</em
      ></span
    > subtrees each of of depth <span class="math"
    ><em
      >n</em
      ></span
    > is <span class="math"
    ><em
      >n</em
      > + 1</span
    >.</p
  ><p
  >We can also generalize this <code
    >VecTree</code
    > type by replacing <code
    >Vec b</code
    > with an arbitrary functor <code
    >f</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >FTree</span
      > <span class="dv"
      >&#8759;</span
      > (<span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      >) &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >               a &#8594; <span class="dt"
      >FTree</span
      > f <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > f (<span class="dt"
      >FTree</span
      > f n) a &#8594; <span class="dt"
      >FTree</span
      > f (<span class="dt"
      >S</span
      > n) a<br
       /><br
       /><span class="kw"
      >type</span
      > <span class="dt"
      >VecTree</span
      > b <span class="fu"
      >=</span
      > <span class="dt"
      >FTree</span
      > (<span class="dt"
      >Vec</span
      > b)<br
       /></code
    ></pre
  ><p
  >Better yet, introduce an intermediate generalization, using the property that <code
    >Vec b &#8801; Trie (BNat b)</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > <span class="dt"
      >TrieTree</span
      > i <span class="fu"
      >=</span
      > <span class="dt"
      >FTree</span
      > (<span class="dt"
      >Trie</span
      > i)<br
       /><br
       /><span class="kw"
      >type</span
      > <span class="dt"
      >VecTree</span
      > b <span class="fu"
      >=</span
      > <span class="dt"
      >TrieTree</span
      > (<span class="dt"
      >BNat</span
      > b)<br
       /></code
    ></pre
  ><p
  >With the exception of the most general form (<code
    >FTree</code
    >), these trees are also tries.</p
  ></div
>

<div id="generalizing-and-inverting-our-trees"
><h3
  >Generalizing and inverting our trees</h3
  ><p
  >The <code
    >FTree</code
    > type looks very like another data type that came up above, namely right-folded <span class="math"
    ><em
      >b</em
      ></span
    >-ary functor composition:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > (<span class="fu"
      >:^</span
      >) <span class="dv"
      >&#8759;</span
      > (<span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      >) &#8594; <span class="fu"
      >*</span
      > &#8594; (<span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      >) <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >ZeroC</span
      > <span class="dv"
      >&#8759;</span
      >             a  &#8594; (f <span class="fu"
      >:^</span
      > <span class="dt"
      >Z</span
      >    ) a<br
       />  <span class="dt"
      >SuccC</span
      > <span class="dv"
      >&#8759;</span
      > f ((f <span class="fu"
      >:^</span
      > n) a) &#8594; (f <span class="fu"
      >:^</span
      > (<span class="dt"
      >S</span
      > n)) a<br
       /></code
    ></pre
  ><p
  >These two types are not just similar; they're identical (different only in naming, i.e., <span class="math"
    >α</span
    >-equivalent), so we can use <code
    >f :^ n</code
    > in place of <code
    >FTree f n</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > <span class="dt"
      >TrieTree</span
      > i <span class="fu"
      >=</span
      > (<span class="fu"
      >:^</span
      >) (<span class="dt"
      >Trie</span
      > i)<br
       /></code
    ></pre
  ><p
  >Instead of <em
    >right-folded</em
    > functor composition, we could go with left-folded. What difference would it make to our notions of <span class="math"
    ><em
      >b</em
      ></span
    >-ary or binary trees?</p
  ><p
  >First look at (right-folded) <code
    >BinTree</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >                  a &#8594; <span class="dt"
      >BinTree</span
      > <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Pair</span
      > (<span class="dt"
      >BinTree</span
      > n) a &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Equivalently,</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >                         a &#8594; <span class="dt"
      >BinTree</span
      > <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >BNat</span
      > <span class="dt"
      >TwoT</span
      > &#8603; <span class="dt"
      >BinTree</span
      > n a) &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >With left-folding, the <code
    >Branch</code
    > constructors would be</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >BinTree</span
      > n (<span class="dt"
      >Pair</span
      > a) &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >or</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >BinTree</span
      > n (<span class="dt"
      >BNat</span
      > <span class="dt"
      >TwoT</span
      > &#8603; a) &#8594; <span class="dt"
      >BinTree</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Then (right-folded) <code
    >VecTree</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >VecTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >                        a &#8594; <span class="dt"
      >VecTree</span
      > b <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >BNat</span
      > b &#8603; <span class="dt"
      >VecTree</span
      > b n a) &#8594; <span class="dt"
      >VecTree</span
      > b (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >Equivalently,</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >VecTree</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >Leaf</span
      >   <span class="dv"
      >&#8759;</span
      >                    a  &#8594; <span class="dt"
      >VecTree</span
      > b <span class="dt"
      >Z</span
      >     a<br
       />  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Vec</span
      > b (<span class="dt"
      >VecTree</span
      > b n a) &#8594; <span class="dt"
      >VecTree</span
      > b (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >With left-folding:</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >VecTree</span
      > b n (<span class="dt"
      >BNat</span
      > b &#8603; a) &#8594; <span class="dt"
      >VecTree</span
      > b (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >or</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >Branch</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >VecTree</span
      > b n (<span class="dt"
      >Vec</span
      > b a) &#8594; <span class="dt"
      >VecTree</span
      > b (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >In shifting from right- to left-folding, our tree structuring becomes inverted. Now a &quot;<span class="math"
    ><em
      >b</em
      ></span
    >-ary&quot; tree really has only <em
    >one</em
    > subtree per branch node, not <span class="math"
    ><em
      >b</em
      ></span
    > subtrees.</p
  ><p
  >For instance, right-folded a binary tree of depth two might look like</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >Branch</span
      > (<span class="dt"
      >Branch</span
      > (<span class="dt"
      >Leaf</span
      > <span class="dv"
      >0</span
      >, <span class="dt"
      >Leaf</span
      > <span class="dv"
      >1</span
      >), <span class="dt"
      >Branch</span
      > (<span class="dt"
      >Leaf</span
      > <span class="dv"
      >2</span
      >, <span class="dt"
      >Leaf</span
      > <span class="dv"
      >3</span
      >))<br
       /></code
    ></pre
  ><p
  >For readability, I'm using normal pairs instead of 2-vectors or <code
    >Pair</code
    > pairs here. In contrast, the corresponding left-folded a binary tree would look like</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >Branch</span
      > (<span class="dt"
      >Branch</span
      > (<span class="dt"
      >Leaf</span
      > ((<span class="dv"
      >0</span
      >,<span class="dv"
      >1</span
      >),(<span class="dv"
      >2</span
      >,<span class="dv"
      >3</span
      >))))<br
       /></code
    ></pre
  ><p
  >Note that the <code
    >VecTree</code
    > constructors are in a linear chain, forming an outer shell, and the number of such constructors is the one more than the depth, and hence logarithmic in the number of leaves. The right-folded form has <code
    >VecTree</code
    > constructors scattered throughout the tree, and the number of such constructors is exponential in the depth, and hence linear in the number of leaves. (As Sebastian Fischer pointed out, however, the number of <em
    >pairing</em
    > constructors is not reduced in the left-folded form.)</p
  ><p
  >For more examples of this sort of inversion, see Chris Okasaki's gem of a paper <a href="http://www.eecs.usma.edu/webs/people/okasaki/pubs.html#icfp99" title="Paper by Chris Okasaki"
    ><em
      >From Fast Exponentiation to Square Matrices</em
      ></a
    >.</p
  ></div
>

<div id="what-sort-of-trees-do-we-have"
><h3
  >What sort of trees do we have?</h3
  ><p
  >I pulled a bit of a bait-and-switch above in reformulating trees. The initial infinite tree type had values <em
    >and</em
    > branching at every node:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >BinTree</span
      > a (<span class="dt"
      >BinTree</span
      > a) (<span class="dt"
      >BinTree</span
      > a)<br
       /></code
    ></pre
  ><p
  >In contrast, the depth-typed trees (whether binary, <span class="math"
    ><em
      >b</em
      ></span
    >-ary, trie-ary, or functor-ary) all have strict separation of leaf nodes from branching nodes.</p
  ><p
  >A conventional, finite binary tree data type might look like</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >Leaf</span
      > a <span class="fu"
      >|</span
      > <span class="dt"
      >Branch</span
      > (<span class="dt"
      >Pair</span
      > (<span class="dt"
      >BinTree</span
      > a))<br
       /></code
    ></pre
  ><p
  >Its inverted (left-folded) form:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >BinTree</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >Leaf</span
      > a <span class="fu"
      >|</span
      > <span class="dt"
      >Branch</span
      > (<span class="dt"
      >BinTree</span
      > (<span class="dt"
      >Pair</span
      > a))<br
       /></code
    ></pre
  ><p
  >When we had depth typing, the right- and left-folded forms were equally expressive. They both described &quot;perfect&quot; trees, with each depth consisting entirely of branching or (for the deepest level) entirely of values. (I'm speaking figuratively for the left-folded case, since literally there is no branching.)</p
  ><p
  >Without depth typing, the expressiveness differs significantly. Right-folded trees can be ragged, with leaves occurring at various depths in the same tree. Left-folded binary trees of depth <span class="math"
    ><em
      >n</em
      ></span
    > can only be perfect, even though the depth is determined dynamically, not statically (i.e., not from type).</p
  ><p
  >Dynamically-depthed binary trees generalize to <span class="math"
    ><em
      >b</em
      ></span
    >-ary, trie-ary, and functor-ary versions. In each case, the left-folded versions are much more statically constrained than their right-folded counterparts.</p
  ></div
>

<div id="from-here"
><h3
  >From here</h3
  ><p
  >This post is the last of a six-part series on tries and static size-typing in the context of numbers, vectors, and trees. Maybe you're curious where these ideas came from and where they might be going.</p
  ><p
  >I got interested in these relationships while noodling over some imperative, data-parallel programs. I asked one one of my standard questions: What elegant beauty is hiding deep beneath these low-level implementation details. In this case, prominent details include array indices, bit fiddling, and power-of-two restrictions, which led me to play with binary numbers. Moreover, parallel algorithms often use a divide-and-conquer strategy. That strategy hints at balanced binary trees, which then can be indexed by binary numbers (bit sequences). Indexing brought memo tries to mind.</p
  ><p
  >I expect to write soon about some ideas &amp; techniques for deriving low-level, side-effecting, parallel algorithms from semantically simple and elegant specifications.</p
  ></div
>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=314&amp;md5=209bdcd3e572f837b906074e674313d9"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/from-tries-to-trees/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Ffrom-tries-to-trees&amp;language=en_GB&amp;category=text&amp;title=From+tries+to+trees&amp;description=This+post+is+the+last+of+a+series+of+six+relating+numbers%2C+vectors%2C+and+trees%2C+revolving+around+the+themes+of+static+size-typing+and+memo+tries.+We%26%238217%3Bve+seen+that+length-typed+vectors...&amp;tags=number%2Ctree%2Ctrie%2Cvector%2Cblog" type="text/html" />
	</item>
		<item>
		<title>A trie for length-typed vectors</title>
		<link>http://conal.net/blog/posts/a-trie-for-length-typed-vectors</link>
		<comments>http://conal.net/blog/posts/a-trie-for-length-typed-vectors#comments</comments>
		<pubDate>Mon, 31 Jan 2011 23:03:48 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[number]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[vector]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=308</guid>
		<description><![CDATA[As you might have noticed, I&#8217;ve been thinking and writing about memo tries lately. I don&#8217;t mean to; they just keep coming up. Memoization is the conversion of functions to data structures. A simple, elegant, and purely functional form of memoization comes from applying three common type isomorphisms, which also correspond to three laws of [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- teaser -->

<p
>As you might have noticed, I&#8217;ve been thinking and writing about <a href="http://conal.net/blog/tag/memoization/" title="Posts on memoization"
  >memo</a
  > <a href="http://conal.net/blog/tag/trie/" title="Posts on tries"
  >tries</a
  > lately. I don&#8217;t mean to; they just keep coming up.</p
>

<p
>Memoization is the conversion of functions to data structures. A simple, elegant, and purely functional form of memoization comes from applying three common type isomorphisms, which also correspond to three laws of exponents, familiar from high school math, as noted by Ralf Hinze in his paper <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.8.4069" title="Paper by Ralf Hinze"
  ><em
    >Generalizing Generalized Tries</em
    ></a
  >.</p
>

<p
>In Haskell, one can neatly formulate memo tries via an associated functor, <code
  >Trie</code
  >, with a convenient synonym &quot;<code
  >k &#8603; v</code
  >&quot; for <code
  >Trie k v</code
  >, as in <a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
  ><em
    >Elegant memoization with higher-order types</em
    ></a
  >. (Note that I&#8217;ve changed my pretty-printing from &quot;<code
  >k :&#8594;: v</code
  >&quot; to &quot;<code
  >k &#8603; v</code
  >&quot;.) The key property is that the data structure encodes (is isomorphic to) a function, i.e.,</p
>

<pre class="sourceCode haskell"
><code
  >k &#8603; a <span class="dt"
    >&#8773;</span
    > k &#8594; a<br
     /></code
  ></pre
>

<p
>In most cases, we ignore non-strictness, though there is <a href="http://conal.net/blog/posts/nonstrict-memoization/" title="blog post"
  >a delightful solution</a
  > for memoizing non-strict functions correctly.</p
>

<p
><a href="http://conal.net/blog/posts/type-bounded-numbers/" title="blog post"
  >My</a
  > <a href="http://conal.net/blog/posts/fixing-lists/" title="blog post"
  >previous</a
  > <a href="http://conal.net/blog/posts/doing-more-with-length-typed-vectors/" title="blog post"
  >four</a
  > <a href="http://conal.net/blog/posts/reverse-engineering-length-typed-vectors/" title="blog post"
  >posts</a
  > explored use of types to statically bound numbers and to determine lengths of vectors.</p
>

<p
>Just as (infinite-only) streams are the natural trie for unary natural numbers, we saw in <a href="http://conal.net/blog/posts/reverse-engineering-length-typed-vectors/" title="blog post"
  ><em
    >Reverse-engineering length-typed vectors</em
    ></a
  > that length-typed vectors (one-dimensional arrays) are the natural trie for statically <em
  >bounded</em
  > natural numbers.</p
>

<pre class="sourceCode haskell"
><code
  ><span class="dt"
    >BNat</span
    > n &#8603; a &#8801; <span class="dt"
    >Vec</span
    > n a<br
     /></code
  ></pre
>

<p
>and so</p
>

<pre class="sourceCode haskell"
><code
  ><span class="dt"
    >BNat</span
    > n &#8594; a <span class="dt"
    >&#8773;</span
    > <span class="dt"
    >Vec</span
    > n a<br
     /></code
  ></pre
>

<p
>In retrospect, this relationship is completely unsurprising, since a vector of length <span class="math"
  ><em
    >n</em
    ></span
  > is a collection of values, indexed by <span class="math"
  >0, . . . , <em
    >n</em
    > - 1</span
  >.</p
>

<p
>In that <a href="http://conal.net/blog/posts/reverse-engineering-length-typed-vectors/" title="blog post"
  >same post</a
  >, I noted that vectors are not only a trie for bounded numbers, but when the elements are also bounded numbers, the vectors can also be thought of <em
  >as numbers</em
  >. Both the number of digits and the number base are captured statically, in types:</p
>

<pre class="sourceCode haskell"
><code
  ><span class="kw"
    >type</span
    > <span class="dt"
    >Digits</span
    > n b <span class="fu"
    >=</span
    > <span class="dt"
    >Vec</span
    > n (<span class="dt"
    >BNat</span
    > b)<br
     /></code
  ></pre
>

<p
>The type parameters <code
  >n</code
  > and <code
  >b</code
  > here are type-encodigs of unary numbers, i.e., built up from zero and successor (<code
  >Z</code
  > and <code
  >S</code
  >). For instance, when <code
  >b &#8801; S (S Z)</code
  >, we have <span class="math"
  ><em
    >n</em
    ></span
  >-bit binary numbers.</p
>

<p
>In this new post, I look at another question of tries and vectors. Given that <code
  >Vec n</code
  > is the trie for <code
  >BNat n</code
  >, is there also a trie for <code
  >Vec n</code
  >?</p
>

<p
><strong
  >Edits:</strong
  ></p
>

<ul
><li
  >2011-01-31: Switched trie notation to &quot;<code
    >k &#8603; v</code
    >&quot; to avoid missing character on iPad.</li
  ></ul
>

<p><span id="more-308"></span></p>

<div id="a-trie-for-length-typed-vectors"
><h3
  >A trie for length-typed vectors</h3
  ><p
  >A vector is a trie over bounded numbers. What is a trie over vectors? As always, isomorphisms show us the way.</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >Vec</span
      > n a &#8594; b <span class="dt"
      >&#8773;</span
      > (a &#215; &#8943; &#215; a) &#8594; b<br
       />             <span class="dt"
      >&#8773;</span
      > a &#8594; &#8943; &#8594; a &#8594; b<br
       />             <span class="dt"
      >&#8773;</span
      > a &#8603; &#8943; &#8603; a &#8603; b<br
       />             &#8801; <span class="dt"
      >Trie</span
      > a (&#8943; (<span class="dt"
      >Trie</span
      > a b)&#8943;)<br
       />             <span class="dt"
      >&#8773;</span
      > (<span class="dt"
      >Trie</span
      > a &#8728; &#8943; &#8728; <span class="dt"
      >Trie</span
      > a) b<br
       /></code
    ></pre
  ><p
  >So the trie (functor) for <code
    >Vec n a</code
    > is the <span class="math"
    ><em
      >n</em
      ></span
    >-ary composition of tries for <code
    >a</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > <span class="dt"
      >VTrie</span
      > n a <span class="fu"
      >=</span
      > <span class="dt"
      >Trie</span
      > a <span class="fu"
      >:^</span
      > n <br
       /></code
    ></pre
  ><p
  >where for any functor <code
    >f</code
    > and natural number type <code
    >n</code
    >,</p
  ><pre class="sourceCode haskell"
  ><code
    >f <span class="fu"
      >:^</span
      > n <span class="dt"
      >&#8773;</span
      > f &#8728; &#8943; &#8728; f  <span class="co"
      >-- (n times)</span
      ><br
       /></code
    ></pre
  ></div
>

<div id="n-ary-functor-composition"
><h3
  >N-ary functor composition</h3
  ><p
  >Since composition is associative, a recursive formulation might naturally fold from the left or from right. (Or perhaps in a balanced tree, to facilitate parallel execution.)</p
  ><div id="right-folded-composition"
  ><h4
    >Right-folded composition</h4
    ><p
    >Let's look at each fold direction, starting with the right, i.e.,</p
    ><pre class="sourceCode haskell"
    ><code
      >f <span class="fu"
    >:^</span
    > <span class="dt"
    >Z</span
    >   <span class="dt"
    >&#8773;</span
    > <span class="dt"
    >Id</span
    ><br
     />f <span class="fu"
    >:^</span
    > <span class="dt"
    >S</span
    > n <span class="dt"
    >&#8773;</span
    > f &#8728; (f <span class="fu"
    >:^</span
    > n)<br
     /></code
      ></pre
    ><p
    >Writing as a GADT:</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >data</span
    > (<span class="fu"
    >:^</span
    >) <span class="dv"
    >&#8759;</span
    > (<span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    >) &#8594; <span class="fu"
    >*</span
    > &#8594; (<span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    >) <span class="kw"
    >where</span
    ><br
     />  <span class="dt"
    >ZeroC</span
    > <span class="dv"
    >&#8759;</span
    >                        a  &#8594; (f <span class="fu"
    >:^</span
    > <span class="dt"
    >Z</span
    >) a<br
     />  <span class="dt"
    >SuccC</span
    > <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >IsNat</span
    > n &#8658; f ((f <span class="fu"
    >:^</span
    > n) a) &#8594; (f <span class="fu"
    >:^</span
    > (<span class="dt"
    >S</span
    > n)) a<br
     /></code
      ></pre
    ><p
    >Functors compose into functors and applicatives into applicatives. (See <a href="http://www.soi.city.ac.uk/~ross/papers/Applicative.html" title="Paper by Conor McBride and Ross Paterson"
      ><em
    >Applicative Programming with Effects</em
    ></a
      > (section 5) and the instance definitions in <a href="http://conal.net/blog/posts/semantic-editor-combinators/" title="blog post"
      ><em
    >Semantic editor combinators</em
    ></a
      >.) The following definitions arise from the standard instances for binary functor composition.</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >instance</span
    > <span class="kw"
    >Functor</span
    > f &#8658; <span class="kw"
    >Functor</span
    > (f <span class="fu"
    >:^</span
    > n) <span class="kw"
    >where</span
    ><br
     />  <span class="fu"
    >fmap</span
    > h (<span class="dt"
    >ZeroC</span
    > a)  <span class="fu"
    >=</span
    > <span class="dt"
    >ZeroC</span
    > (h a)<br
     />  <span class="fu"
    >fmap</span
    > h (<span class="dt"
    >SuccC</span
    > fs) <span class="fu"
    >=</span
    > <span class="dt"
    >SuccC</span
    > ((fmap&#8728;fmap) h fs)<br
     /></code
      ></pre
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >instance</span
    > (<span class="dt"
    >IsNat</span
    > n, <span class="dt"
    >Applicative</span
    > f) &#8658; <span class="dt"
    >Applicative</span
    > (f <span class="fu"
    >:^</span
    > n) <span class="kw"
    >where</span
    ><br
     />  pure <span class="fu"
    >=</span
    > pureN nat<br
     />  <span class="dt"
    >ZeroC</span
    > f  &#8859; <span class="dt"
    >ZeroC</span
    > x  <span class="fu"
    >=</span
    > <span class="dt"
    >ZeroC</span
    > (f x)<br
     />  <span class="dt"
    >SuccC</span
    > fs &#8859; <span class="dt"
    >SuccC</span
    > xs <span class="fu"
    >=</span
    > <span class="dt"
    >SuccC</span
    > (liftA2 (&#8859;) fs xs)<br
     /></code
      ></pre
    ><!--

>   _ <*> _ = error "ComposeFunctor.<*>: can't happen" {- why needed? -}

 --><pre class="sourceCode haskell"
    ><code
      >pureN <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >Applicative</span
    > f &#8658; <span class="dt"
    >Nat</span
    > n &#8594; a &#8594; (f <span class="fu"
    >:^</span
    > n) a<br
     />pureN <span class="dt"
    >Zero</span
    >     a <span class="fu"
    >=</span
    > <span class="dt"
    >ZeroC</span
    > a<br
     />pureN (<span class="dt"
    >Succ</span
    > _) a <span class="fu"
    >=</span
    > <span class="dt"
    >SuccC</span
    > ((pure &#8728; pure) a)<br
     /></code
      ></pre
    ><p
    >More explicitly, the second <code
      >pure</code
      > could instead use <code
      >pureN</code
      >:</p
    ><pre class="sourceCode haskell"
    ><code
      >pureN (<span class="dt"
    >Succ</span
    > n) a <span class="fu"
    >=</span
    > <span class="dt"
    >SuccC</span
    > ((pure &#8728; pureN n) a)<br
     /></code
      ></pre
    ><div id="some-tidier-definitions"
    ><h5
      >Some tidier definitions</h5
      ><p
      >Using <code
    >(&#8852;)</code
    >, there are tidier definitions of <code
    >fmap</code
    > and <code
    >(&#8859;)</code
    >:</p
      ><pre class="sourceCode haskell"
      ><code
    >  <span class="fu"
      >fmap</span
      > <span class="fu"
      >=</span
      > inZeroC  (<span class="fu"
      >$</span
      >) &#8852; inSuccC  (fmap&#8728;fmap)<br
       /><br
       />  (&#8859;)  <span class="fu"
      >=</span
      > inZeroC2 (<span class="fu"
      >$</span
      >) &#8852; inSuccC2 (liftA2 (&#8859;))<br
       /></code
    ></pre
      ><p
      >where the new combinators are <em
    >partial</em
    > functions that work inside of <code
    >ZeroC</code
    > and <code
    >SuccC</code
    >.</p
      ><pre class="sourceCode haskell"
      ><code
    >inZeroC  h (<span class="dt"
      >ZeroC</span
      > a ) <span class="fu"
      >=</span
      > <span class="dt"
      >ZeroC</span
      > (h a )<br
       /><br
       />inSuccC  h (<span class="dt"
      >SuccC</span
      > as) <span class="fu"
      >=</span
      > <span class="dt"
      >SuccC</span
      > (h as)<br
       /></code
    ></pre
      ><pre class="sourceCode haskell"
      ><code
    >inZeroC2 h (<span class="dt"
      >ZeroC</span
      > a ) (<span class="dt"
      >ZeroC</span
      > b ) <span class="fu"
      >=</span
      > <span class="dt"
      >ZeroC</span
      > (h a  b )<br
       /><br
       />inSuccC2 h (<span class="dt"
      >SuccC</span
      > as) (<span class="dt"
      >SuccC</span
      > bs) <span class="fu"
      >=</span
      > <span class="dt"
      >SuccC</span
      > (h as bs)<br
       /></code
    ></pre
      ><p
      >This example demonstrates another notational benefit of <code
    >(&#8852;)</code
    >, extending the techniques in the post <a href="http://conal.net/blog/posts/lazier-function-definitions-by-merging-partial-values/" title="blog post"
    ><em
      >Lazier function definitions by merging partial values</em
      ></a
    >.</p
      ></div
    ></div
  ><div id="left-folded-composition"
  ><h4
    >Left-folded composition</h4
    ><p
    >For left-folded composition, a tiny change suffices in the <code
      >S</code
      > case:</p
    ><pre class="sourceCode haskell"
    ><code
      >f <span class="fu"
    >:^</span
    > <span class="dt"
    >Z</span
    >   <span class="dt"
    >&#8773;</span
    > <span class="dt"
    >Id</span
    ><br
     />f <span class="fu"
    >:^</span
    > <span class="dt"
    >S</span
    > n <span class="dt"
    >&#8773;</span
    > (f <span class="fu"
    >:^</span
    > n) &#8728; f<br
     /></code
      ></pre
    ><p
    >which translates to a correspondingly tiny change in the <code
      >SuccC</code
      > constructor.</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >data</span
    > (<span class="fu"
    >:^</span
    >) <span class="dv"
    >&#8759;</span
    > (<span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    >) &#8594; <span class="fu"
    >*</span
    > &#8594; (<span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    >) <span class="kw"
    >where</span
    ><br
     />  <span class="dt"
    >ZeroC</span
    > <span class="dv"
    >&#8759;</span
    >                        a  &#8594; (f <span class="fu"
    >:^</span
    > <span class="dt"
    >Z</span
    >) a<br
     />  <span class="dt"
    >SuccC</span
    > <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >IsNat</span
    > n &#8658; (f <span class="fu"
    >:^</span
    > n) (f a) &#8594; (f <span class="fu"
    >:^</span
    > (<span class="dt"
    >S</span
    > n)) a<br
     /></code
      ></pre
    ><p
    >The <code
      >Functor</code
      > and <code
      >Applicative</code
      > instances are completely unchanged.</p
    ></div
  ></div
>

<div id="vector-tries-continued"
><h3
  >Vector tries (continued)</h3
  ><p
  >Using the analysis above, we can easily define tries over vectors as <span class="math"
    ><em
      >n</em
      ></span
    >-ary composition of tries over the vector element type. Again, there is a right-folded and a left-folded version.</p
  ><div id="right-folded-vector-tries"
  ><h4
    >Right-folded vector tries</h4
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >instance</span
    > (<span class="dt"
    >IsNat</span
    > n, <span class="dt"
    >HasTrie</span
    > a) &#8658; <span class="dt"
    >HasTrie</span
    > (<span class="dt"
    >Vec</span
    > n a) <span class="kw"
    >where</span
    ><br
     />  <span class="kw"
    >type</span
    > <span class="dt"
    >Trie</span
    > (<span class="dt"
    >Vec</span
    > n a) <span class="fu"
    >=</span
    > <span class="dt"
    >Trie</span
    > a <span class="fu"
    >:^</span
    > n<br
     /></code
      ></pre
    ><p
    >Conversion from trie to function is, as always, a trie look-up. Its definition closely follows the definition of <code
      >f :^ n</code
      >:</p
    ><pre class="sourceCode haskell"
    ><code
      >  <span class="dt"
    >ZeroC</span
    > v <span class="ot"
    >`untrie`</span
    > <span class="dt"
    >ZVec</span
    >      <span class="fu"
    >=</span
    > v<br
     />  <span class="dt"
    >SuccC</span
    > t <span class="ot"
    >`untrie`</span
    > (a <span class="fu"
    >:&lt;</span
    > as) <span class="fu"
    >=</span
    > (t <span class="ot"
    >`untrie`</span
    > a) <span class="ot"
    >`untrie`</span
    > as<br
     /></code
      ></pre
    ><!--

<   _ `untrie` _ = error "untrie on Vec n a: Can't happen" {- why nec? -}

<   enumerate = error "enumerate: not yet defined on Vec n a"

 --><p
    >For <code
      >untrie</code
      >, we were able to follow the zero/successor structure of the trie. For <code
      >trie</code
      >, we don't have such a structure to follow, but we can play the same trick as for defining <code
      >units</code
      > above. Use the <code
      >nat</code
      > method of the <code
      >IsNat</code
      > class to synthesize a number of type <code
      >Nat n</code
      >, and then follow the structure of that number in a new recursive function definition.</p
    ><pre class="sourceCode haskell"
    ><code
      >  trie <span class="fu"
    >=</span
    > trieN nat<br
     /></code
      ></pre
    ><p
    >where</p
    ><pre class="sourceCode haskell"
    ><code
      >trieN <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >HasTrie</span
    > a &#8658; <span class="dt"
    >Nat</span
    > n &#8594; (<span class="dt"
    >Vec</span
    > n a &#8594; b) &#8594; (<span class="dt"
    >Trie</span
    > a <span class="fu"
    >:^</span
    > n) b<br
     />trieN <span class="dt"
    >Zero</span
    >     f <span class="fu"
    >=</span
    > <span class="dt"
    >ZeroC</span
    > (f <span class="dt"
    >ZVec</span
    >)<br
     />trieN (<span class="dt"
    >Succ</span
    > _) f <span class="fu"
    >=</span
    > <span class="dt"
    >SuccC</span
    > (trie (&#955; a &#8594; trie (f &#8728; (a <span class="fu"
    >:&lt;</span
    >))))<br
     /></code
      ></pre
    ></div
  ><div id="left-folded-vector-tries"
  ><h4
    >Left-folded vector tries</h4
    ><p
    >The change from right-folding to left-folding is minuscule.</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >instance</span
    > (<span class="dt"
    >IsNat</span
    > n, <span class="dt"
    >HasTrie</span
    > a) &#8658; <span class="dt"
    >HasTrie</span
    > (<span class="dt"
    >Vec</span
    > n a) <span class="kw"
    >where</span
    ><br
     />  <span class="kw"
    >type</span
    > <span class="dt"
    >Trie</span
    > (<span class="dt"
    >Vec</span
    > n a) <span class="fu"
    >=</span
    > <span class="dt"
    >Trie</span
    > a <span class="fu"
    >:^</span
    > n<br
     /><br
     />  <span class="dt"
    >ZeroC</span
    > b <span class="ot"
    >`untrie`</span
    > <span class="dt"
    >ZVec</span
    >      <span class="fu"
    >=</span
    > b<br
     />  <span class="dt"
    >SuccC</span
    > t <span class="ot"
    >`untrie`</span
    > (a <span class="fu"
    >:&lt;</span
    > as) <span class="fu"
    >=</span
    > (t <span class="ot"
    >`untrie`</span
    > as) <span class="ot"
    >`untrie`</span
    > a<br
     />  <br
     />  trie <span class="fu"
    >=</span
    > trieN nat<br
     /></code
      ></pre
    ><pre class="sourceCode haskell"
    ><code
      >trieN <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >HasTrie</span
    > a &#8658; <span class="dt"
    >Nat</span
    > n &#8594; (<span class="dt"
    >Vec</span
    > n a &#8594; b) &#8594; (<span class="dt"
    >Trie</span
    > a <span class="fu"
    >:^</span
    > n) b<br
     />trieN <span class="dt"
    >Zero</span
    >     f <span class="fu"
    >=</span
    > <span class="dt"
    >ZeroC</span
    > (f <span class="dt"
    >ZVec</span
    >)<br
     />trieN (<span class="dt"
    >Succ</span
    > _) f <span class="fu"
    >=</span
    > <span class="dt"
    >SuccC</span
    > (trie (&#955; as &#8594; trie (f &#8728; (<span class="fu"
    >:&lt;</span
    > as))))<br
     /></code
      ></pre
    ></div
  ><div id="right-vs-left"
  ><h4
    >Right vs Left?</h4
    ><p
    >There are two tries for <code
      >Vec n a</code
      >, but with Haskell we have to choose one as <em
      >the</em
      > <code
      >HasTrie</code
      > instance. How can we make our choice?</p
    ><p
    >The same sort of question arises earlier. The post <a href="http://conal.net/blog/posts/reverse-engineering-length-typed-vectors/" title="blog post"
      ><em
    >Reverse-engineering length-typed vectors</em
    ></a
      > showed how to discover <code
      >Vec n</code
      > by looking for the trie functor for the <code
      >BNat n</code
      >. The derivation was</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="dt"
    >Vec</span
    > n a <span class="dt"
    >&#8773;</span
    > a &#215; &#8943; &#215; a<br
     />        <span class="dt"
    >&#8773;</span
    > (<span class="dv"
    >1</span
    > &#8594; a) &#215; &#8943; &#215; (<span class="dv"
    >1</span
    > &#8594; a)<br
     />        <span class="dt"
    >&#8773;</span
    > (<span class="dv"
    >1</span
    > <span class="fu"
    >+</span
    > &#8943; <span class="fu"
    >+</span
    > <span class="dv"
    >1</span
    >) &#8594; a<br
     /></code
      ></pre
    ><p
    >The question of associativity arises here as well, for both product and sum. The <code
      >BNat n a</code
      > and <code
      >Vec n</code
      > types both choose right-associativity:</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >data</span
    > <span class="dt"
    >BNat</span
    > <span class="dv"
    >&#8759;</span
    > <span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    > <span class="kw"
    >where</span
    ><br
     />  <span class="dt"
    >BZero</span
    > <span class="dv"
    >&#8759;</span
    >          <span class="dt"
    >BNat</span
    > (<span class="dt"
    >S</span
    > n)<br
     />  <span class="dt"
    >BSucc</span
    > <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >BNat</span
    > n &#8594; <span class="dt"
    >BNat</span
    > (<span class="dt"
    >S</span
    > n)<br
     /><br
     /><span class="kw"
    >data</span
    > <span class="dt"
    >Vec</span
    > <span class="dv"
    >&#8759;</span
    > <span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    > <span class="kw"
    >where</span
    ><br
     />  <span class="dt"
    >ZVec</span
    > <span class="dv"
    >&#8759;</span
    >                <span class="dt"
    >Vec</span
    > <span class="dt"
    >Z</span
    >     a<br
     />  (<span class="fu"
    >:&lt;</span
    >) <span class="dv"
    >&#8759;</span
    > a &#8594; <span class="dt"
    >Vec</span
    > n a &#8594; <span class="dt"
    >Vec</span
    > (<span class="dt"
    >S</span
    > n) a<br
     /></code
      ></pre
    ><p
    >Since trie construction is type-driven, I see the vector trie based on <em
      >right-folded</em
      > composition as in line with these definitions. Left-folding would come from a small initial change, swapping the constructors in the <code
      >BNat</code
      > definition:</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >data</span
    > <span class="dt"
    >BNat</span
    > <span class="dv"
    >&#8759;</span
    > <span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    > <span class="kw"
    >where</span
    ><br
     />  <span class="dt"
    >BSucc</span
    > <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >BNat</span
    > n &#8594; <span class="dt"
    >BNat</span
    > (<span class="dt"
    >S</span
    > n)<br
     />  <span class="dt"
    >BZero</span
    > <span class="dv"
    >&#8759;</span
    >          <span class="dt"
    >BNat</span
    > (<span class="dt"
    >S</span
    > n)<br
     /></code
      ></pre
    ><p
    >From this tweaked beginning, a <code
      >BNat</code
      > trie construction would lead to a left-associated <code
      >Vec</code
      > type:</p
    ><pre class="sourceCode haskell"
    ><code
      ><span class="kw"
    >data</span
    > <span class="dt"
    >Vec</span
    > <span class="dv"
    >&#8759;</span
    > <span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    > &#8594; <span class="fu"
    >*</span
    > <span class="kw"
    >where</span
    ><br
     />  (<span class="fu"
    >:&lt;</span
    >) <span class="dv"
    >&#8759;</span
    > <span class="dt"
    >Vec</span
    > n a &#8594; a &#8594; <span class="dt"
    >Vec</span
    > (<span class="dt"
    >S</span
    > n) a<br
     />  <span class="dt"
    >ZVec</span
    > <span class="dv"
    >&#8759;</span
    >                <span class="dt"
    >Vec</span
    > <span class="dt"
    >Z</span
    >     a<br
     /></code
      ></pre
    ><p
    >And then left-folded composition for the <code
      >Vec</code
      > trie.</p
    ></div
  ></div
>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=308&amp;md5=c22f70ca5eb996033fdb76a4a76593ff"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/a-trie-for-length-typed-vectors/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fa-trie-for-length-typed-vectors&amp;language=en_GB&amp;category=text&amp;title=A+trie+for+length-typed+vectors&amp;description=As+you+might+have+noticed%2C+I%26%238217%3Bve+been+thinking+and+writing+about+memo+tries+lately.+I+don%26%238217%3Bt+mean+to%3B+they+just+keep+coming+up.+Memoization+is+the+conversion+of+functions+to...&amp;tags=number%2Ctrie%2Cvector%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Fixing lists</title>
		<link>http://conal.net/blog/posts/fixing-lists</link>
		<comments>http://conal.net/blog/posts/fixing-lists#comments</comments>
		<pubDate>Sun, 30 Jan 2011 18:14:30 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[applicative functor]]></category>
		<category><![CDATA[functor]]></category>
		<category><![CDATA[number]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[vector]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=284</guid>
		<description><![CDATA[In the post Memoizing polymorphic functions via unmemoization, I toyed with the idea of lists as tries. I don&#8217;t think [a] is a trie, simply because [a] is a sum type (being either nil or a cons), while tries are built out of the identity, product, and composition functors. In contrast, Stream is a trie, [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- teaser -->

<p
>In the post <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization/" title="blog post"
  ><em
    >Memoizing polymorphic functions via unmemoization</em
    ></a
  >, I toyed with the idea of lists as tries. I don&#8217;t think <code
  >[a]</code
  > is a trie, simply because <code
  >[a]</code
  > is a <em
  >sum</em
  > type (being either nil or a cons), while tries are built out of the identity, product, and composition functors. In contrast, <code
  >Stream</code
  > <em
  >is</em
  > a trie, being built solely with the identity and product functors. Moreover, <code
  >Stream</code
  > is not just any old trie, it is the trie that corresponds to Peano (unary natural) numbers, i.e., <code
  >Stream a &#8773; N &#8594; a</code
  >, where</p
>

<pre class="sourceCode haskell"
><code
  ><span class="kw"
    >data</span
    > <span class="dt"
    >N</span
    > <span class="fu"
    >=</span
    > <span class="dt"
    >Zero</span
    > <span class="fu"
    >|</span
    > <span class="dt"
    >Succ</span
    > <span class="dt"
    >N</span
    ><br
     /><br
     /><span class="kw"
    >data</span
    > <span class="dt"
    >Stream</span
    > a <span class="fu"
    >=</span
    > <span class="dt"
    >Cons</span
    > a (<span class="dt"
    >Stream</span
    > a)<br
     /></code
  ></pre
>

<p
>If we didn't already know the <code
  >Stream</code
  > type, we would derive it systematically from <code
  >N</code
  >, using standard isomorphisms.</p
>

<p
><code
  >Stream</code
  > is a trie (over unary numbers), thanks to it having no choice points, i.e., no sums in its construction. However, streams are infinite-only, which is not always what we want. In contrast, lists can be finite, but are not a trie in any sense I understand. In this post, I look at how to <em
  >fix</em
  > lists, so they can be finite and yet be a trie, thanks to having no choice points (sums)?</p
>

<p
>You can find the code for this post and the <a href="http://conal.net/blog/posts/type-bounded-numbers/" title="blog post"
  >previous one</a
  > in a <a href="https://github.com/conal/numbers-vectors-trees/" title="github repository"
  >code repository</a
  >.</p
>

<p
><strong
  >Edits</strong
  >:</p
>

<ul
><li
  >2011-01-30: Added spoilers warning.</li
  ><li
  >2011-01-30: Pointer to <a href="https://github.com/conal/numbers-vectors-trees/" title="github repository"
    >code repository</a
    >.</li
  ></ul
>

<p><span id="more-284"></span></p>

<div id="fixing-lists"
><h3
  >Fixing lists</h3
  ><p
  >Is there a type of finite lists without choice points (sums)? Yes. There are lots of them. One for each length. Instead of having a single type of lists, have an infinite family of types of <span class="math"
    ><em
      >n</em
      ></span
    >-element lists, one type for each <span class="math"
    ><em
      >n</em
      ></span
    >.</p
  ><p
  >In other words, to fix the problem with lists (trie-unfriendliness), split up the usual list type into subtypes (so to speak), each of which has a fixed length.</p
  ><p
  >I realize I'm changing the question to a simpler one. I hope you'll forgive me and hang in to see where this ride goes.</p
  ><p
  >As a first try, we might use tuples as our fixed-length lists:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > <span class="dt"
      >L0</span
      > a <span class="fu"
      >=</span
      > ()<br
       /><span class="kw"
      >type</span
      > <span class="dt"
      >L1</span
      > a <span class="fu"
      >=</span
      > (a)<br
       /><span class="kw"
      >type</span
      > <span class="dt"
      >L2</span
      > a <span class="fu"
      >=</span
      > (a,a)<br
       /><span class="kw"
      >type</span
      > <span class="dt"
      >L3</span
      > a <span class="fu"
      >=</span
      > (a,a,a)<br
       />&#8943;<br
       /></code
    ></pre
  ><p
  >However, we can only write down finitely many such types, and I don't know how we could write any definitions that are polymorphic over <em
    >length</em
    >.</p
  ><p
  >What can &quot;polymorphic over length&quot; mean in a setting like Haskell, where polymorphism is over <em
    >types</em
    > rather than <em
    >values</em
    >. Can we express numbers (for lengths, etc) as types? Yes, as in the previous post, <a href="http://conal.net/blog/posts/type-bounded-numbers/" title="blog post"
    ><em
      >Type-bounded numbers</em
      ></a
    >, using a common encoding:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >Z</span
      >    <span class="co"
      >-- zero</span
      ><br
       /><span class="kw"
      >data</span
      > <span class="dt"
      >S</span
      > n  <span class="co"
      >-- successor</span
      ><br
       /></code
    ></pre
  ><p
  >Given these type-level numbers, we can define a data type <code
    >Vec n a</code
    >, containing only vectors (fixed lists) of length <code
    >n</code
    > and elements of type <code
    >a</code
    >. Such vectors can be built up as either the zero-length vector, or by adding an element to an vector of length <span class="math"
    ><em
      >n</em
      ></span
    > to get a vector of length <span class="math"
    ><em
      >n</em
      > + 1</span
    >. I don't know how to define this type as a regular algebraic data type, but it's easy as a <em
    >generalized</em
    > algebraic data type (<a href="http://en.wikibooks.org/wiki/Haskell/GADT" title="Haskell Wikibook page"
    >GADT</a
    >):</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >infixr</span
      > <span class="dv"
      >5</span
      > <span class="fu"
      >:&lt;</span
      ><br
       /><br
       /><span class="kw"
      >data</span
      > <span class="dt"
      >Vec</span
      > <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      > <span class="kw"
      >where</span
      ><br
       />  <span class="dt"
      >ZVec</span
      > <span class="dv"
      >&#8759;</span
      >                <span class="dt"
      >Vec</span
      > <span class="dt"
      >Z</span
      >     a<br
       />  (<span class="fu"
      >:&lt;</span
      >) <span class="dv"
      >&#8759;</span
      > a &#8594; <span class="dt"
      >Vec</span
      > n a &#8594; <span class="dt"
      >Vec</span
      > (<span class="dt"
      >S</span
      > n) a<br
       /></code
    ></pre
  ><p
  >For example,</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="fu"
      >*</span
      ><span class="dt"
      >Vec</span
      ><span class="fu"
      >&gt;</span
      > <span class="fu"
      >:</span
      >ty <span class="ch"
      >'z'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="ch"
      >'o'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="ch"
      >'m'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="ch"
      >'g'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="dt"
      >ZVec</span
      ><br
       /><span class="ch"
      >'z'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="ch"
      >'o'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="ch"
      >'m'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="ch"
      >'g'</span
      > <span class="fu"
      >:&lt;</span
      > <span class="dt"
      >ZVec</span
      > <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Vec</span
      > (<span class="dt"
      >S</span
      > (<span class="dt"
      >S</span
      > (<span class="dt"
      >S</span
      > (<span class="dt"
      >S</span
      > <span class="dt"
      >Z</span
      >)))) <span class="dt"
      >Char</span
      ><br
       /></code
    ></pre
  ><p
  >As desired, <code
    >Vec</code
    > is length-typed, covers all (finite) lengths, and allows definition of length-polymorphic functions. For instance, it's easy to map functions over vectors:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >instance</span
      > <span class="kw"
      >Functor</span
      > (<span class="dt"
      >Vec</span
      > n) <span class="kw"
      >where</span
      ><br
       />  <span class="fu"
      >fmap</span
      > _ <span class="dt"
      >ZVec</span
      >     <span class="fu"
      >=</span
      > <span class="dt"
      >ZVec</span
      ><br
       />  <span class="fu"
      >fmap</span
      > f (a <span class="fu"
      >:&lt;</span
      > u) <span class="fu"
      >=</span
      > f a <span class="fu"
      >:&lt;</span
      > <span class="fu"
      >fmap</span
      > f u<br
       /></code
    ></pre
  ><p
  >The type of <code
    >fmap</code
    > here is <code
    >(a &#8594; b) &#8594; Vec n a &#8594; Vec n b</code
    >.</p
  ><p
  >Folding over vectors is also straightforward:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >instance</span
      > <span class="dt"
      >Foldable</span
      > (<span class="dt"
      >Vec</span
      > n) <span class="kw"
      >where</span
      ><br
       />  <span class="fu"
      >foldr</span
      > _ b <span class="dt"
      >ZVec</span
      >      <span class="fu"
      >=</span
      > b<br
       />  <span class="fu"
      >foldr</span
      > h b (a <span class="fu"
      >:&lt;</span
      > as) <span class="fu"
      >=</span
      > a <span class="ot"
      >`h`</span
      > <span class="fu"
      >foldr</span
      > h b as<br
       /></code
    ></pre
  ><p
  >Is <code
    >Vec n</code
    > an applicative functor as well?</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >instance</span
      > <span class="dt"
      >Applicative</span
      > (<span class="dt"
      >Vec</span
      > n) <span class="kw"
      >where</span
      ><br
       />  &#8943;<br
       /></code
    ></pre
  ><p
  >We would need</p
  ><pre class="sourceCode haskell"
  ><code
    >pure <span class="dv"
      >&#8759;</span
      > a &#8594; <span class="dt"
      >Vec</span
      > n a<br
       />(&#8859;)  <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Vec</span
      > n (a &#8594; b) &#8594; <span class="dt"
      >Vec</span
      > n a &#8594; <span class="dt"
      >Vec</span
      > n b<br
       /></code
    ></pre
  ><p
  >The <code
    >(&#8859;)</code
    > method can be defined similarly to <code
    >fmap</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    >  <span class="dt"
      >ZVec</span
      >      &#8859; <span class="dt"
      >ZVec</span
      >      <span class="fu"
      >=</span
      > <span class="dt"
      >ZVec</span
      ><br
       />  (f <span class="fu"
      >:&lt;</span
      > fs) &#8859; (x <span class="fu"
      >:&lt;</span
      > xs) <span class="fu"
      >=</span
      > f x <span class="fu"
      >:&lt;</span
      > (fs &#8859; xs)<br
       /></code
    ></pre
  ><p
  >Unlike <code
    >fmap</code
    > and <code
    >(&#8859;)</code
    >, <code
    >pure</code
    > doesn't have a vector structure to crawl over. It must create just the right structure anyway. You might enjoy thinking about how to solve this puzzle, which I'll tackle in my next post. (Warning: spoilers in the comments below.)</p
  ></div
>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=284&amp;md5=8a876a1ca74b5e365684d743df52c81c"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/fixing-lists/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Ffixing-lists&amp;language=en_GB&amp;category=text&amp;title=Fixing+lists&amp;description=In+the+post+Memoizing+polymorphic+functions+via+unmemoization%2C+I+toyed+with+the+idea+of+lists+as+tries.+I+don%26%238217%3Bt+think+%5Ba%5D+is+a+trie%2C+simply+because+%5Ba%5D+is+a+sum...&amp;tags=applicative+functor%2Cfunctor%2Cnumber%2Ctrie%2Cvector%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Fixing broken isomorphisms &#8212; details for non-strict memoization, part 2</title>
		<link>http://conal.net/blog/posts/fixing-broken-isomorphisms-details-for-non-strict-memoization-part-2</link>
		<comments>http://conal.net/blog/posts/fixing-broken-isomorphisms-details-for-non-strict-memoization-part-2#comments</comments>
		<pubDate>Wed, 22 Sep 2010 23:02:26 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[functor]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[unamb]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=198</guid>
		<description><![CDATA[The post Details for non-strict memoization, part 1 works out a systematic way of doing non-strict memoization, i.e., correct memoization of non-strict (and more broadly, non-hyper-strict) functions. As I mentioned at the end, there was an awkward aspect, which is that the purported &#8220;isomorphisms&#8221; used for regular types are not quite isomorphisms. For instance, functions [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Fixing broken isomorphisms - details for non-strict memoization, part 2

Tags: memoization, functor, trie, unamb

URL: http://conal.net/blog/posts/fixing-broken-isomorphisms-details-for-non-strict-memoization-part-2/

-->

<!-- references -->

<!-- teaser -->

<!--
**Edits**:

* 2010-02-09: just fiddling around
-->

<!-- without a comment or something here, the last item above becomes a paragraph -->

<p>The post <em><a href="http://conal.net/blog/posts/details-for-nonstrict-memoization-part-1/" title="blog post">Details for non-strict memoization, part 1</a></em> works out a systematic way of doing <em>non-strict</em> memoization, i.e., correct memoization of non-strict (and more broadly, non-hyper-strict) functions.
As I mentioned at the end, there was an awkward aspect, which is that the purported &#8220;isomorphisms&#8221; used for regular types are not quite isomorphisms.</p>

<p>For instance, functions from triples are memoized by converting to and from nested pairs:</p>

<pre><code>untriple ∷ (a,b,c) -&gt; ((a,b),c)
untriple (a,b,c) = ((a,b),c)

triple ∷ ((a,b),c) -&gt; (a,b,c)
triple ((a,b),c) = (a,b,c)
</code></pre>

<p>Then <code>untriple</code> and <code>triple</code> form an embedding/projection pair, i.e.,</p>

<pre><code>triple ∘ untriple ≡ id
untriple ∘ triple ⊑ id
</code></pre>

<p>The reason for the inequality is that the nested-pair form permits <code>(⊥,c)</code>, which does not correspond to any triple.</p>

<pre><code>untriple (triple (⊥,c)) ≡ untriple ⊥ ≡ ⊥
</code></pre>

<p>Can we patch this problem by simply using an irrefutable (lazy) pattern in the definition of <code>triple</code>, i.e., <code>triple (~(a,b),c) = (a,b,c)</code>?
Let&#8217;s try:</p>

<pre><code>untriple (triple (⊥,c)) ≡ untriple (⊥,⊥,c) ≡ ((⊥,⊥),c)
</code></pre>

<p>So isomorphism fails and so does even the embedding/projection property.</p>

<p>Similarly, to deal with regular algebraic data types, I used a class that describes regular data types as repeated applications of a single, associated <em>pattern functor</em> (following <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.142.4778" title="Paper by Thomas Noort, Alexey Rodriguez, Stefan Holdermans, Johan Jeuring, Bastiaan Heeren">A Lightweight Approach to Datatype-Generic Rewriting</a></em>):</p>

<pre><code>class Functor (PF t) ⇒ Regular t where
  type PF t ∷ * → *
  unwrap ∷ t → PF t t
  wrap   ∷ PF t t → t
</code></pre>

<p>Here <code>unwrap</code> converts a value into its pattern functor form, and <code>wrap</code> converts back.
For example, here is the <code>Regular</code> instance I had used for lists:</p>

<pre><code>instance Regular [a] where
  type PF [a] = Const () :+: Const a :*: Id

  unwrap []     = InL (Const ())
  unwrap (a:as) = InR (Const a :*: Id as)

  wrap (InL (Const ()))          = []
  wrap (InR (Const a :*: Id as)) = a:as
</code></pre>

<p>Again, we have an embedding/projection pair, rather than a genuine isomorphism:</p>

<pre><code>wrap ∘ unwrap ≡ id
unwrap ∘ wrap ⊑ id
</code></pre>

<p>The inequality comes from ⊥ values occurring in <code>PF [a] [a]</code> at type <code>Const () [a]</code>, <code>()</code>,  <code>(Const a :*: Id) [a]</code>, <code>Const a [a]</code>, or <code>Id [a]</code>.</p>

<p><span id="more-198"></span></p>

<h3>Why care?</h3>

<p>What harm results from the lack of genuine isomorphism?
For hyper-strict functions, as usually handled (correctly) in memoization, I don&#8217;t think there is any harm.
For correct memoization of non-hyper-strict functions, however, the superfluous points of undefinedness lead to larger memo tries and wasted effort.
For instance, a function from triples goes through some massaging on the way to being memoized:</p>

<pre><code>λ (a,b,c) → ⋯
⇓
λ ((a,b),c) → ⋯
⇓
λ (a,b) → λ c → ⋯
</code></pre>

<p>For hyper-strict memoization, the next step transforms to <code>λ a → λ b → λ c → ⋯</code>.
For non-strict memoization, however, we first stash away the value of the function applied to <code>⊥ ∷ (a,b)</code>, which will always be ⊥ in this context.</p>

<h3>Strict products and sums</h3>

<p>To eliminate the definedness discrepancy and regain isomorphism, we might make all non-strictness explicit via unlifted product &amp; sums, and explicit lifting.</p>

<pre><code>-- | Add a bottom to a type
data Lift a = Lift { unLift ∷ a } deriving Functor

infixl 6 :+:!
infixl 7 :*:!

-- | Strict pair
data a :*! b = !a :*! !b

-- | Strict sum
data a :+! b = Left' !a | Right' !b
</code></pre>

<p>Note that the <code>Id</code> and <code>Const a</code> functors used in canonical representations are already strict, as they&#8217;re defined via <code>newtype</code>.</p>

<p>With these new tools, we can decompose isomorphically.
For instance,</p>

<pre><code>(a,b,c) ≅ Lift a :*! Lift b :*! Lift c
</code></pre>

<p>with the isomorphism given by</p>

<pre><code>untriple' ∷ (a,b,c) -&gt; Lift a :*! Lift b :*! Lift c
untriple' (a,b,c) = Lift a :*! Lift b :*! Lift c

triple' ∷ Lift a :*! Lift b :*! Lift c -&gt; (a,b,c)
triple' (Lift a :*! Lift b :*! Lift c) = (a,b,c)
</code></pre>

<p>For regular types, we&#8217;ll also want variations as functor combinators:</p>

<pre><code>-- | Strict product functor
data (f :*:! g) a = !(f a) :*:! !(g a) deriving Functor

-- | Strict sum functor
data (f :+:! g) a = InL' !(f a) | InR' !(g a) deriving Functor
</code></pre>

<p>Then change the <code>Regular</code> instance on lists to the following:</p>

<pre><code>instance Regular [a] where
  type PF [a] = Const () :+:! Const (Lift a) :*:! Lift

  unwrap []     = InL' (Const ())
  unwrap (a:as) = InR' (Const (Lift a) :*:! Lift as)

  wrap (InL' (Const ()))                    = []
  wrap (InR' (Const (Lift a) :*:! Lift as)) = a:as
</code></pre>

<p>I suppose it would be fairly straightforward to derive such instances for algebraic data types automatically via Template Haskell.</p>

<h3>Tries for non-strict memoization</h3>

<p>As in <em><a href="http://conal.net/blog/posts/details-for-nonstrict-memoization-part-1/" title="blog post">part 1</a></em>, represent a non-strict memo trie for a function <code>f ∷ k -&gt; v</code> as a value for <code>f ⊥</code> and a <em>strict</em> (but not hyper-strict) memo trie for <code>f</code>:</p>

<pre><code>type k :→: v = Trie v (k :→ v)
</code></pre>

<p>For non-strict sum domains, the strict memo trie was a pair of non-strict tries:</p>

<pre><code>instance (HasTrie a, HasTrie b) ⇒ HasTrie (Either a b) where
  type STrie (Either a b) = Trie a :*: Trie b
  sTrie   f           = trie (f ∘ Left) :*: trie (f ∘ Right)
  sUntrie (ta :*: tb) = untrie ta `either` untrie tb
</code></pre>

<p>For non-strict product, the strict trie was a composition of non-strict tries:</p>

<pre><code>instance (HasTrie a, HasTrie b) =&gt; HasTrie (a , b) where
  type STrie (a , b) = Trie a :. Trie b
  sTrie   f = O (fmap trie (trie (curry f)))
  sUntrie (O tt) = uncurry (untrie (fmap untrie tt))
</code></pre>

<p>What about <em>strict</em> sum and product domains?
Since strict sums &amp; products cannot contain ⊥ as their immediate components, we can omit the values corresponding to ⊥ for those components.
That is, we can use pairs and compositions of <em>strict</em> tries instead.</p>

<pre><code>instance (HasTrie a, HasTrie b) =&gt; HasTrie (a :+! b) where
  type STrie (a :+! b) = STrie a :*: STrie b
  sTrie   f           = sTrie (f . Left') :*: sTrie (f . Right')
  sUntrie (ta :*: tb) = sUntrie ta `either'` sUntrie tb

instance (HasTrie a, HasTrie b) =&gt; HasTrie (a :*! b) where
  type STrie (a :*! b) = STrie a :. STrie b
  sTrie   f      = O (fmap sTrie (sTrie (curry' f)))
  sUntrie (O tt) = uncurry' (sUntrie (fmap sUntrie tt))
</code></pre>

<p>I&#8217;ve also substituted versions of <code>curry</code> and <code>uncurry</code> for strict products and <code>either</code> for strict sums:</p>

<pre><code>curry' ∷ (a :*! b -&gt; c) -&gt; (a -&gt; b -&gt; c)
curry' f a b = f (a :*! b)

uncurry' ∷ (a -&gt; b -&gt; c) -&gt; ((a :*! b) -&gt; c)
uncurry' f (a :*! b) = f a b

either' ∷ (a -&gt; c) -&gt; (b -&gt; c) -&gt; (a :+! b -&gt; c)
either' f _ (Left'  a) = f a
either' _ g (Right' b) = g b
</code></pre>

<p>We&#8217;ll also need to handle the lifting functor.
The type <code>Lift a</code> has an additional bottom.
A strict function or trie over <code>Lift a</code> is only strict in the lower (outer) one.
So a strict trie over <code>Lift a</code> is simply a non-strict trie over <code>a</code>.</p>

<pre><code>instance HasTrie a =&gt; HasTrie (Lift a) where
  type STrie (Lift a) = Trie a
  sTrie   f = trie (f . Lift)
  sUntrie t = untrie t . unLift
</code></pre>

<p>Notice that this instance puts back exactly what was lost from memo tries when going from non-strict products and sums to strict products and sums.
The reason for this relationship is explained in the following simple isomorphisms:</p>

<pre><code>(a,b)      ≅ Lift a :*! Lift b
Either a b ≅ Lift a :+! Lift b
</code></pre>

<p>Then isomorphisms can then be used to implement memoize over non-strict products and sums via memoization over strict products and sums.</p>

<h2>Higher-order memoization</h2>

<p>The post <em><a href="http://conal.net/blog/posts/memoizing-higher-order-functions/" title="blog post">Memoizing higher-order functions</a></em> suggested a simple way to memoize functions over function-valued domains by using (as always) type isomorphisms.
The isomorphism used is between functions and memo tries.</p>

<p>I gave one example in that post</p>

<pre><code>ft1 ∷ (Bool → a) → [a]
ft1 f = [f False, f True]
</code></pre>

<p>In retrospect, this example was a lousy choice, as it hides an important problem.
The <code>Bool</code> type is <em>finite</em>, and so the corresponding trie type has only finitely large elements.
For that reason, higher-order memoization can get away with the usual hyper-strict memoization.</p>

<p>If instead, we try memoizing a function of type <code>(a → b) → c</code>, where the type <code>a</code> has infinitely many <code>elements</code> (e.g., <code>Integer</code> or <code>[Bool]</code>), then we&#8217;ll have to memoize over the domain <code>a :→: b</code> (memo tries from <code>a</code> to <code>b</code>), which includes infinite elements.
In that case, hyper-strict memoization blows up, so we&#8217;ll want to use non-strict memoization instead.</p>

<p>As mentioned above, the type of non-strict tries contains a value and a strict trie:</p>

<pre><code>type k :→: v = Trie v (k :→ v)
</code></pre>

<p>I thought I&#8217;d memoize by mapping to &amp; from the isomorphic pair type <code>(v, k :→ v)</code>.
However, now I&#8217;m not satisfied with this mapping.
A non-strict trie from <code>k</code> to <code>v</code> is not just <em>any</em> such pair of <code>v</code> and <code>k :→ v</code>.
Monotonicity requires that the single <code>v</code> value (for ⊥) be a lower bound (information-wise) of every <code>v</code> in the trie.
Ignoring this constraint would lead to a trie in which most of the entries do not correspond to any non-strict memo trie.</p>

<p><em>Puzzle:</em> Can this constraint be captured as a <em>static</em> type in modern Haskell&#8217;s (GHC&#8217;s) type system (i.e., without resorting to general dependent typing)?
I don&#8217;t know the answer.</p>

<h2>Memoizing abstract types</h2>

<p>This problem is more wide-spread still.
Whenever there are constraints on a representation beyond what is expressed directly and statically in the representation type, we will have this same sort of isomorphism puzzle.
Can we capture the constraint as a Haskell type?
When we cannot, what do we do?</p>

<p>If we didn&#8217;t care about efficiency, I think we could ignore the issue, and everything else in this blog post, and accept making memo tries that are much larger than necessary.
Although laziness will keep from filling in range values for unaccessed domain values, I worry that there will be quite a lot time and space wasted navigating past large portions of unusable trie structure.</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=198&amp;md5=c2a469f4293d7f7d52b9fe50951c2f43"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/fixing-broken-isomorphisms-details-for-non-strict-memoization-part-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Ffixing-broken-isomorphisms-details-for-non-strict-memoization-part-2&amp;language=en_GB&amp;category=text&amp;title=Fixing+broken+isomorphisms+%26%238212%3B+details+for+non-strict+memoization%2C+part+2&amp;description=The+post+Details+for+non-strict+memoization%2C+part+1+works+out+a+systematic+way+of+doing+non-strict+memoization%2C+i.e.%2C+correct+memoization+of+non-strict+%28and+more+broadly%2C+non-hyper-strict%29+functions.+As+I+mentioned...&amp;tags=functor%2Cmemoization%2Ctrie%2Cunamb%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Details for non-strict memoization, part 1</title>
		<link>http://conal.net/blog/posts/details-for-nonstrict-memoization-part-1</link>
		<comments>http://conal.net/blog/posts/details-for-nonstrict-memoization-part-1#comments</comments>
		<pubDate>Tue, 27 Jul 2010 00:14:20 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[functor]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[unamb]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=131</guid>
		<description><![CDATA[In Non-strict memoization, I sketched out a means of memoizing non-strict functions. I gave the essential insight but did not show the details of how a nonstrict memoization library comes together. In this new post, I give details, which are a bit delicate, in terms of the implementation described in Elegant memoization with higher-order types. [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Details for non-strict memoization, part 1

Tags: memoization, functor, trie, unamb

URL: http://conal.net/blog/posts/details-for-nonstrict-memoization-part-1/

-->

<!-- references -->

<!-- teaser -->

<p>In <em><a href="http://conal.net/blog/posts/nonstrict-memoization/" title="blog post">Non-strict memoization</a></em>, I sketched out a means of memoizing non-strict functions.
I gave the essential insight but did not show the details of how a nonstrict memoization library comes together.
In this new post, I give details, which are a bit delicate, in terms of the implementation described in <em><a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post">Elegant memoization with higher-order types</a></em>.</p>

<p>Near the end, I run into some trouble with regular data types, which I don&#8217;t know how to resolve cleanly and efficiently.</p>

<p><strong>Edits</strong>:</p>

<ul>
<li>2010-09-10: Fixed minor typos.</li>
</ul>

<!-- without a comment or something here, the last item above becomes a paragraph -->

<p><span id="more-131"></span></p>

<h3>Hyper-strict memo tries</h3>

<p>Strict memoization (really <em>hyper-strict</em>) is centered on a family of trie functors, defined as a functor <code>Trie k</code>, associated with a type <code>k</code>.</p>

<pre><code>type k :→: v = Trie k v

class HasTrie k where
    type Trie k :: * → *
    trie   :: (k  →  v) → (k :→: v)
    untrie :: (k :→: v) → (k  →  v)
</code></pre>

<p>The simplest instance is for the unit type:</p>

<pre><code>instance HasTrie () where
  type Trie ()  = Id
  trie   f      = Id (f ())
  untrie (Id v) = λ () → v
</code></pre>

<p>For consistency with other types, I just made a small change from the previous version, which used <code>const v</code> instead of the stricter <code>λ () → v</code>.</p>

<p>Sums and products are a little more intricate:</p>

<pre><code>instance (HasTrie a, HasTrie b) ⇒ HasTrie (Either a b) where
  type Trie (Either a b) = Trie a :*: Trie b
  trie   f           = trie (f ∘ Left) :*: trie (f ∘ Right)
  untrie (ta :*: tb) = untrie ta `either` untrie tb

instance (HasTrie a, HasTrie b) ⇒ HasTrie (a , b) where
  type Trie (a , b) = Trie a :. Trie b
  trie   f = O (trie (trie ∘ curry f))
  untrie (O tt) = uncurry (untrie ∘ untrie tt)
</code></pre>

<p>These trie types are not just strict, they&#8217;re <em>hyper-strict</em>.
During trie search, arguments get thorougly evaluated.
(See Section 9 in the paper <em><a href="http://conal.net/blog/posts/denotational-design-with-type-class-morphisms/" title="blog post">Denotational design with type class morphisms</a></em>.)
In other words, all of the points of possible undefinedness are lost.</p>

<h3>Strict and non-strict memo tries</h3>

<p>The formulation of strict tries will look very like the hyper-strict tries we&#8217;ve already seen, with new names for the associated trie type and the conversion methods:</p>

<pre><code>type k :→ v = STrie k v

class HasTrie k where
    type STrie k :: * → *
    sTrie   ::             (k  → v) → (k :→ v)
    sUntrie :: HasLub v ⇒ (k :→ v) → (k  → v)
</code></pre>

<p>Besides renaming, I&#8217;ve also added a <code>HasLub</code> constraint for <code>sUntrie</code>, which we&#8217;ll need later.</p>

<p>For instance, the (almost) simplest strict trie is the one for the unit type, defined exactly as before (with new names):</p>

<pre><code>instance HasTrie () where
  type STrie ()  = Id
  sTrie   f      = Id (f ())
  sUntrie (Id v) = λ () → v
</code></pre>

<p>For <em>non-strict</em> memoization, we&#8217;ll want to recover all of the points of possible undefinedness lost in hyper-strict memoization.
At every level of a structured value, there is the possibility of ⊥ or of a non-⊥ value.
Correspondingly, a non-strict trie consists of the value corresponding to the argument ⊥, together with a strict (but <em>not</em> hyper-strict) trie for the non-⊥ values:</p>

<pre><code>data Trie k v = Trie v (k :→ v)

type k :→: v = Trie k v
</code></pre>

<p>The conversions between functions and non-strict tries are no longer methods, as they can be defined uniformly for all domain types.
To form a non-strict trie, capture the function&#8217;s value at ⊥, and build a strict (but not hyper-strict) trie:</p>

<pre><code>trie   :: (HasTrie k          ) ⇒ (k  →  v) → (k :→: v)
trie f = Trie (f ⊥) (sTrie f)
</code></pre>

<p>To convert back from a non-strict trie to a (now memoized) function, combine the information from two sources: the original function&#8217;s value at ⊥, and the function resulting from the strict (but not hyper-strict) trie:</p>

<pre><code>untrie :: (HasTrie k, HasLub v) ⇒ (k :→: v) → (k  →  v)
untrie (Trie b t) = const b ⊔ sUntrie t
</code></pre>

<p>The least-upper-bound (⊔) here is well-defined because its arguments are information-compatible (consistent, non-contradictory).
More strongly, <code>const b ⊑ sUntrie t</code>, i.e., the first argument is an information approximation to (contains no information absent from) the second argument.
Now we see the need for <code>HasLub v</code> in the type of <code>sUntrie</code> above: functions are ⊔-able exactly when their result types are.</p>

<h3>Sums</h3>

<p>Just as non-strict tries contain strict tries, so also strict tries contain non-strict tries.
For instance, consider a sum type, <code>Either a b</code>.
An element is either ⊥ or <code>Left x</code> or <code>Right y</code>, for <code>x :: a</code> and <code>y :: b</code>.
The types <code>a</code> and <code>b</code> also contain a bottom element, so we&#8217;ll need non-strict memo tries for them:</p>

<pre><code>instance (HasTrie a, HasTrie b) ⇒ HasTrie (Either a b) where
  type STrie (Either a b) = Trie a :*: Trie b
  sTrie   f           = trie (f ∘ Left) :*: trie (f ∘ Right)
  sUntrie (ta :*: tb) = untrie ta `either` untrie tb
</code></pre>

<p>Just as in the unit instance (above), the only visible change from hyper-strict to strict is that the left-hand sides use the strict trie type and operations.
The right-hand sides are written exactly as before, though now they refer to non-strict tries and their operations.</p>

<h3>Products</h3>

<p>With product, we run into some trouble.
As a first attempt, change only the names on the left-hand side:</p>

<pre><code>instance (HasTrie a, HasTrie b) ⇒ HasTrie (a , b) where
  type STrie (a , b) = Trie a :. Trie b
  sTrie   f      = O (trie (trie ∘ curry f))
  sUntrie (O tt) = uncurry (untrie ∘ untrie tt)
</code></pre>

<p>This <code>sUntrie</code> definition, however, leads to an error in type-checking:</p>

<pre><code>Could not deduce (HasLub (Trie b v)) from the context (HasLub v)
  arising from a use of `untrie'
</code></pre>

<p>The troublesome <code>untrie</code> use is the one applied directly to <code>tt</code>.
(Thank you for column numbers, GHC.)</p>

<p>So what&#8217;s going on here?
Since <code>sUntrie</code> in this definition takes a <code>(a,b) :→ v</code>, or equivalently, <code>STrie (a,b) v</code>,</p>

<pre><code>O tt :: (a,b) :→ v
     :: STrie (a,b) v
     :: (Trie a :. Trie b) v
</code></pre>

<p>The definition of type composition (from <a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post">an earlier post</a>) is</p>

<pre><code>newtype (g :. f) x = O (g (f x))
</code></pre>

<p>So</p>

<pre><code>tt :: Trie a (Trie b v)
   :: a :→: b :→: v
</code></pre>

<p>and</p>

<pre><code>untrie tt :: HasLub (b :→: v) ⇒ a → (b :→: v)
</code></pre>

<p>The <code>HasLub</code> constraint comes from the type of <code>untrie</code> (above).</p>

<p>Continuing,</p>

<pre><code>untrie ∘ untrie tt ::
  (HasLub v, HasLub (b :→: v)) ⇒ a → (b → v)

uncurry (untrie ∘ untrie tt) ::
  (HasLub v, HasLub (b :→: v)) ⇒ (a , b) → v
</code></pre>

<p>which is <em>almost</em> the required type but contains the extra requirement that <code>HasLub (b :→: v)</code>.</p>

<p>Hm.</p>

<p>Looking at the definition of <code>Trie</code> and the definitions of <code>STrie</code> for various domain types <code>b</code>, I think it&#8217;s the case that <code>HasLub (b :→: v)</code>, whenever <code>HasLub v</code>, exactly as needed.
In principle, I could make this requirement of <code>b</code> explicit as a superclass for <code>HasTrie</code>:</p>

<pre><code>class (forall v. HasLub v ⇒ HasLub (b :→: v)) ⇒ HasTrie k where ...
</code></pre>

<p>However, Haskell&#8217;s type system isn&#8217;t quite expressive enough, even with GHC extensions (as far as I know).</p>

<h4>A possible solution</h4>

<p>We could instead define a functor-level variant of <code>HasLub</code>:</p>

<pre><code>class HasLubF f where
  lubF :: HasLub v ⇒ f v → f v → f v
</code></pre>

<p>and then use <code>lubF</code> instead of <code>(⊔)</code> in <code>sUntrie</code>.
The revised <code>HasTrie</code> class definition:</p>

<pre><code>class HasLubF (Trie k) ⇒ HasTrie k where
    type STrie k :: * → *
    sTrie   ::             (k  → v) → (k :→ v)
    sUntrie :: HasLub v ⇒ (k :→ v) → (k  → v)
</code></pre>

<p>I would rather not replicate and modify the <code>HasLub</code> class and all of its instances, so I&#8217;m going to set this idea aside and look for another.</p>

<h4>Another route</h4>

<p>Let&#8217;s return to the problematic definition of <code>sUntrie</code> for pairs:</p>

<pre><code>sUntrie (O tt) = uncurry (untrie ∘ untrie tt)
</code></pre>

<p>and recall that <code>tt :: a :→: b :→: v</code>.
The strategy here was to first convert the outer trie (with domain <code>a</code>) and then the inner trie (with domain <code>b</code>).</p>

<p>Alternatively, we might reverse the order.</p>

<p>If we&#8217;re going to convert inside-out instead of outside-in, then we&#8217;ll need a way to transform each of the <em>range</em> elements of a trie.
Which is exactly what <code>fmap</code> is for.
If only we had a functor instance for <code>Trie a</code>, then we could re-define <code>sUntrie</code> on pair tries as follows:</p>

<pre><code>sUntrie (O tt) = uncurry (untrie (fmap untrie tt))
</code></pre>

<p>As a sanity check, try compiling this definition.
Sure enough, it&#8217;s okay except for a missing <code>Functor</code> instance:</p>

<pre><code>Could not deduce (Functor (Trie a))
  from the context (HasTrie (a, b), HasTrie a, HasTrie b)
  arising from a use of `fmap'
</code></pre>

<p>Fixed easily enough:</p>

<pre><code>instance Functor (STrie k) ⇒ Functor (Trie k) where
  fmap f (Trie b t) = Trie (f b) (fmap f t)
</code></pre>

<p>Or even, using the GHC language extensions <code>DeriveFunctor</code> and <code>StandaloneDeriving</code>, just</p>

<pre><code>deriving instance Functor (STrie k) ⇒ Functor (Trie k)
</code></pre>

<p>Now we get a slightly different error message.
We&#8217;re now missing a Functor instance for <code>STrie a</code> instead of <code>Trie a</code>:</p>

<pre><code>Could not deduce (Functor (STrie a))
  from the context (HasTrie (a, b), HasTrie a, HasTrie b)
  arising from a use of `fmap'
</code></pre>

<p>By the way, we can also construct tries inside-out, if we want:</p>

<pre><code>sTrie f = O (fmap trie (trie (curry f)))
</code></pre>

<p>So we&#8217;ll be in good shape <em>if</em> we can satisfy the <code>Functor</code> requirement on strict tries.
Fortunately, all of the strict trie (higher-order) types appearing are indeed functors, since we built them up using functor combinators.</p>

<p>Still, we&#8217;ll have to help the type-checker <em>prove</em> that all of the trie types it involved must indeed be functors.
Again, a superclass constraint can capture this requirement:</p>

<pre><code>class Functor (STrie k) ⇒ HasTrie k where ...
</code></pre>

<p>Unlike <code>HasLub</code>, this time the required constraint is already at the functor level, so we don&#8217;t have to define a new class.
We don&#8217;t even have to define any new instances, as our functor combinators come with <code>Functor</code> instances, all of which can be derived automatically by GHC.</p>

<p>With this one change, all of the <code>HasTrie</code> instances go through!</p>

<h3>Isomorphisms</h3>

<p>As pointed out in <em><a href="http://conal.net/blog/posts/memoizing-higher-order-functions/" title="blog post">Memoizing higher-order functions</a></em>, type isomorphism is the central, repeated theme of functional memoization.
In addition to the isomorphism between functions and tries, the tries for many types are given via isomorphism with other types that have tries.
In this way, we only have to define tries for our tiny set of functor combinators.</p>

<p>Isomorphism support is as in <em><a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post">Elegant memoization with higher-order types</a></em>, just using the new names:</p>

<pre><code>#define HasTrieIsomorph(Context,Type,IsoType,toIso,fromIso) 
instance Context ⇒ HasTrie (Type) where { 
  type STrie (Type) = STrie (IsoType); 
  sTrie f = sTrie (f ∘ (fromIso)); 
  sUntrie t = sUntrie t ∘ (toIso); 
}
</code></pre>

<p>Note the use of strict tries even on the right-hand sides.</p>

<p><em>Aside:</em> as mentioned in <em><a href="http://conal.net/blog/posts/composing-memo-tries/" title="blog post">Composing memo tries</a></em>, <code>trie</code>/<code>untrie</code> forms not just an isomorphism but a pair of <a href="http://conal.net/blog/tag/type-class-morphism/" title="Posts on type class morphisms">type class morphism</a>s (TCMs).
(For motivation and examples of TCMs in software design, see <em><a href="http://conal.net/blog/posts/denotational-design-with-type-class-morphisms/" title="blog post">Denotational design with type class morphisms</a></em>.)</p>

<h3>Regular data types</h3>

<p><em>Regular data types</em> are isomorphic to fixed-points of functors.
<em><a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post">Elegant memoization with higher-order types</a></em> gives a brief introduction to these notions and pointers to more information.
That post also shows how to use the <code>Regular</code> type class and its instances (defined for other purposes as well) to provide hyper-strict memo tries for all regular data types.</p>

<p>Switching from hyper-strict to non-strict raises an awkward issue.
The functor isomorphisms we used are only correct for fully defined data-types.
When we allow full or partial undefinedness, as in a lazy language like Haskell, our isomorphisms break down.</p>

<p>Following <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.142.4778" title="Paper by Thomas Noort, Alexey Rodriguez, Stefan Holdermans, Johan Jeuring, Bastiaan Heeren">A Lightweight Approach to Datatype-Generic Rewriting</a></em>, here is the class I used, where &#8220;<code>PF</code>&#8221; stands for &#8220;pattern functor&#8221;:</p>

<pre><code>class Functor (PF t) ⇒ Regular t where
  type PF t :: * → *
  unwrap :: t → PF t t
  wrap   :: PF t t → t
</code></pre>

<p>The <code>unwrap</code> method peels off a single layer from a regular type.
For example, the top level of a list is either a unit (nil) or a pair (cons) of an element and a hole in which a list can be placed.</p>

<pre><code>instance Regular [a] where
  type PF [a] = Unit :+: Const a :*: Id   -- note Unit == Const ()

  unwrap []     = InL (Const ())
  unwrap (a:as) = InR (Const a :*: Id as)

  wrap (InL (Const ()))          = []
  wrap (InR (Const a :*: Id as)) = a:as
</code></pre>

<p>The catch here is that the <code>unwrap</code> and <code>wrap</code> methods do not really form an isomorphism.
Instead, they satisfy a weaker connection: they form embedding/projection pair.
That is,</p>

<pre><code>wrap ∘ unwrap ≡ id
unwrap ∘ wrap ⊑ id
</code></pre>

<p>To see the mismatch between <code>[a]</code> and <code>PF [a] [a]</code>, note that the latter has opportunities for partial undefinedness that have no corresponding opportunities in <code>[a]</code>.
Specifically, ⊥ could occur at type <code>Const () [a]</code>, <code>()</code>,  <code>(Const a :*: Id) [a]</code>, <code>Const a [a]</code>, or <code>Id [a]</code>.
Any of these ⊥ values will result in <code>wrap</code> returning ⊥ altogether.
For instance, if</p>

<pre><code>oops :: PF [Integer]
oops = InR (⊥ :*: Id [3,5])
</code></pre>

<p>then</p>

<pre><code>unwrap (wrap oops) ≡ unwrap ⊥ ≡ ⊥ ⊑ oops
</code></pre>

<p>By examining various cases, we can prove that <code>unwrap (wrap p) ⊑ p</code> for all <code>p</code>, which is to say <code>unwrap ∘ wrap ⊑ id</code>, since
information ordering on functions is defined point-wise.
(See <em><a href="http://conal.net/blog/posts/merging-partial-values/" title="blog post">Merging partial values</a></em>.)</p>

<p>Examining the definition of <code>unwrap</code> above shows that it does not give rise to the troublesome ⊥ points, and so a trivial equational proof shows that <code>wrap ∘ unwrap ≡ id</code>.</p>

<p>In the context of memoization, the additional undefined values are problematic.
Consider the case of lists.
The specification macro</p>

<pre><code>HasTrieRegular1([], ListSTrie)
</code></pre>

<p>expands into a <code>newtype</code> and its <code>HasTrie</code> instance.
Changing only the associated type and method names in the <a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post">version for hyper-strict memoization</a>:</p>

<pre><code>newtype ListSTrie a v = ListSTrie (PF [a] [a] :→: v)

instance HasTrie a ⇒ HasTrie [a] where
  type STrie [a] = ListSTrie a
  sTrie f = ListSTrie (sTrie (f . wrap))
  sUntrie (ListSTrie t) = sUntrie t . unwrap
</code></pre>

<p>Note that the trie in <code>ListSTrie</code> trie contains entries for many ⊥ sub-elements that do not correspond to any list values.
The memoized function is <code>f ∘ wrap</code>, which will have many fewer ⊥ possibilities than the trie structure supports.
At each of the superfluous ⊥ points, the function sampled is strict, so the <code>Trie</code> (rather than <code>STrie</code>) will contain a predictable ⊥.
Considering the definition of <code>untrie</code>:</p>

<pre><code>untrie (Trie b t) = const b ⊔ sUntrie t
</code></pre>

<p>we know <code>b ≡ ⊥</code>, and so <code>const b ⊔ sUntrie t ≡ sUntrie t</code>.
Thus, at these points, the ⊥ value is never helpful, and we could use a strict (though not hyper-strict) trie instead of a non-strict trie.</p>

<p>Perhaps we could safely ignore this whole issue and lose only some efficiency, rather than correctness.
Still, I&#8217;d rather build and traverse just the right trie for our regular types.</p>

<p>As this post is already longer than I intended, and my attention is wandering, I&#8217;ll publish it here and pick up later.
Comments &amp; suggestions please!</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=131&amp;md5=21d7a6c4a22de6280e624f13d3efce1f"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/details-for-nonstrict-memoization-part-1/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fdetails-for-nonstrict-memoization-part-1&amp;language=en_GB&amp;category=text&amp;title=Details+for+non-strict+memoization%2C+part+1&amp;description=In+Non-strict+memoization%2C+I+sketched+out+a+means+of+memoizing+non-strict+functions.+I+gave+the+essential+insight+but+did+not+show+the+details+of+how+a+nonstrict+memoization+library+comes...&amp;tags=functor%2Cmemoization%2Ctrie%2Cunamb%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Memoizing higher-order functions</title>
		<link>http://conal.net/blog/posts/memoizing-higher-order-functions</link>
		<comments>http://conal.net/blog/posts/memoizing-higher-order-functions#comments</comments>
		<pubDate>Wed, 21 Jul 2010 15:41:17 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[isomorphism]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=119</guid>
		<description><![CDATA[Memoization incrementally converts functions into data structures. It pays off when a function is repeatedly applied to the same arguments and applying the function is more expensive than accessing the corresponding data structure. In lazy functional memoization, the conversion from function to data structure happens all at once from a denotational perspective, and incrementally from [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- teaser -->

<p
>Memoization incrementally converts functions into data structures. It pays off when a function is repeatedly applied to the same arguments and applying the function is more expensive than accessing the corresponding data structure.</p
>

<p
>In <em
  >lazy functional</em
  > memoization, the conversion from function to data structure happens all at once from a denotational perspective, and incrementally from an operational perspective. See <em
  ><a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post"
    >Elegant memoization with functional memo tries</a
    ></em
  > and <em
  ><a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
    >Elegant memoization with higher-order types</a
    ></em
  >.</p
>

<p
>As Ralf Hinze presented in <em
  ><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.8.4069" title="Paper: &quot;Generalizing Generalized Tries&quot; by Ralf Hinze"
    >Generalizing Generalized Tries</a
    ></em
  >, trie-based memoization follows from three simple isomorphisms involving functions types:</p
>

<div class=math-inset>
<p
><span class="math"
  >1 → <em
    >a</em
    > ≅ <em
    >a</em
    ></span
  ></p
><p
><span class="math"
  >(<em
    >a</em
    > + <em
    >b</em
    >) → <em
    >c</em
    > ≅ (<em
    >a</em
    > → <em
    >c</em
    >) × (<em
    >b</em
    > → <em
    >c</em
    >)</span
  ></p
><p
><span class="math"
  >(<em
    >a</em
    > × <em
    >b</em
    >) → <em
    >c</em
    > ≅ <em
    >a</em
    > → (<em
    >b</em
    > → <em
    >c</em
    >)</span
  ></p
></div>

<p
>which correspond to the familiar laws of exponents</p
>

<div class=math-inset>
<p
><span class="math"
  >a ^ 1 = a</span
  ></p
><p
><span class="math"
  ><em
    >c</em
    ><sup
    ><em
      >a</em
      > + <em
      >b</em
      ></sup
    > = <em
    >c</em
    ><sup
    ><em
      >a</em
      ></sup
    > × <em
    >c</em
    ><sup
    ><em
      >b</em
      ></sup
    ></span
  ></p
><p
><span class="math"
  ><em
    >c</em
    ><sup
    ><em
      >a</em
      > × <em
      >b</em
      ></sup
    > = (<em
    >c</em
    ><sup
    ><em
      >b</em
      ></sup
    >)<sup
    ><em
      >a</em
      ></sup
    ></span
  ></p
></div>

<p
>When applied as a transformation from left to right, each law simplifies the domain part of a function type. Repeated application of the rules then eliminate all function types or reduce them to functions of atomic types. These atomic domains are eliminated as well by additional mappings, such as between a natural number and a list of bits (as in <a href="http://hackage.haskell.org/packages/archive/containers/0.2.0.1/doc/html/Data-IntMap.html" title="hackage package"
  >patricia trees</a
  >). Algebraic data types corresponding to sums of products and so are eliminated by the sum and product rules. <em
  >Recursive</em
  > algebraic data types (lists, trees, etc) give rise to correspondingly recursive trie types.</p
>

<p
>So, with a few simple and familiar rules, we can memoize functions over an infinite variety of common types. Have we missed any?</p
>

<p
>Yes. <em
  >What about functions over functions?</em
  ></p
>

<p
><strong
  >Edits</strong
  >:</p
>

<ul
><li
  >2010-07-22: Made the memoization example polymorphic and switched from pairs to lists. The old example accidentally coincided with a specialized version of <code
    >trie</code
    > itself.</li
  ><li
  >2011-02-27: updated some notation</li
  ></ul
>

<p><span id="more-119"></span></p>

<div id="tries"
><h3
  >Tries</h3
  ><p
  >In <em
    ><a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
      >Elegant memoization with higher-order types</a
      ></em
    >, I showed a formulation of functional memoization using functor combinators.</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > k &#8603; v <span class="fu"
      >=</span
      > <span class="dt"
      >Trie</span
      > k v<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >class</span
      > <span class="dt"
      >HasTrie</span
      > k <span class="kw"
      >where</span
      ><br
       />  <span class="kw"
      >type</span
      > <span class="dt"
      >Trie</span
      > k <span class="dv"
      >&#8759;</span
      > <span class="fu"
      >*</span
      > &#8594; <span class="fu"
      >*</span
      ><br
       />  trie   <span class="dv"
      >&#8759;</span
      > (k &#8594; v) &#8594; (k &#8603; v)<br
       />  untrie <span class="dv"
      >&#8759;</span
      > (k &#8603; v) &#8594; (k &#8594; v)<br
       /></code
    ></pre
  ><p
  >I will describe higher-order memoization in terms of this formulation. I imagine it would also work out, though less elegantly, in the associated data types formulation described in <em
    ><a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post"
      >Elegant memoization with functional memo tries</a
      ></em
    >.</p
  ></div
>

<div id="domain-isomorphisms"
><h3
  >Domain isomorphisms</h3
  ><p
  ><em
    ><a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
      >Elegant memoization with higher-order types</a
      ></em
    > showed how to define a <code
    >HasTrie</code
    > instance in terms of the instance of an isomorphic type, e.g., reducing tuples to nested pairs or booleans to a sum of unit types. A C macro, <code
    >HasTrieIsomorph</code
    > encapsulates the domain isomorphism technique. For instance, to reduce triples to pairs:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( (<span class="dt"
      >HasTrie</span
      > a, <span class="dt"
      >HasTrie</span
      > b, <span class="dt"
      >HasTrie</span
      > c), (a,b,c), ((a,b),c)<br
       />               , &#955; (a,b,c) &#8594; ((a,b),c), &#955; ((a,b),c) &#8594; (a,b,c))<br
       /></code
    ></pre
  ><p
  >This isomorphism technique applies as well to the standard functor combinators used for constructing tries (any many other purposes). Those combinators again:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >Const</span
      > x a <span class="fu"
      >=</span
      > <span class="dt"
      >Const</span
      > x<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > <span class="dt"
      >Id</span
      > a <span class="fu"
      >=</span
      > <span class="dt"
      >Id</span
      > a<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > (f &#215; g) a <span class="fu"
      >=</span
      > f a &#215; g a<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >data</span
      > (f <span class="fu"
      >+</span
      > g) a <span class="fu"
      >=</span
      > <span class="dt"
      >InL</span
      > (f a) <span class="fu"
      >|</span
      > <span class="dt"
      >InR</span
      > (g a)<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >newtype</span
      > (g &#8728; f) a <span class="fu"
      >=</span
      > <span class="dt"
      >O</span
      > (g (f a))<br
       /></code
    ></pre
  ><p
  >and their trie definitions:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( <span class="dt"
      >HasTrie</span
      > a, <span class="dt"
      >Const</span
      > a x, a, getConst, <span class="dt"
      >Const</span
      > )<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( <span class="dt"
      >HasTrie</span
      > a, <span class="dt"
      >Id</span
      > a, a, unId, <span class="dt"
      >Id</span
      > )<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( (<span class="dt"
      >HasTrie</span
      > (f a), <span class="dt"
      >HasTrie</span
      > (g a))<br
       />               , (f &#215; g) a, (f a,g a)<br
       />               , &#955; (fa &#215; ga) &#8594; (fa,ga), &#955; (fa,ga) &#8594; (fa &#215; ga) )<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( (<span class="dt"
      >HasTrie</span
      > (f a), <span class="dt"
      >HasTrie</span
      > (g a))<br
       />               , (f <span class="fu"
      >+</span
      > g) a, <span class="dt"
      >Either</span
      > (f a) (g a)<br
       />               , eitherF <span class="kw"
      >Left</span
      > <span class="kw"
      >Right</span
      >, <span class="fu"
      >either</span
      > <span class="dt"
      >InL</span
      > <span class="dt"
      >InR</span
      > )<br
       /></code
    ></pre
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( <span class="dt"
      >HasTrie</span
      > (g (f a))<br
       />               , (g &#8728; f) a, g (f a) , unO, <span class="dt"
      >O</span
      > )<br
       /></code
    ></pre
  ><p
  >The <code
    >eitherF</code
    > function is a variation on <a href="http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/Prelude.html#v:either"
    ><code
      >either</code
      ></a
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    >eitherF <span class="dv"
      >&#8759;</span
      > (f a &#8594; b) &#8594; (g a &#8594; b) &#8594; (f <span class="fu"
      >+</span
      > g) a &#8594; b<br
       />eitherF p _ (<span class="dt"
      >InL</span
      > fa) <span class="fu"
      >=</span
      > p fa<br
       />eitherF _ q (<span class="dt"
      >InR</span
      > ga) <span class="fu"
      >=</span
      > q ga<br
       /></code
    ></pre
  ></div
>

<div id="higher-order-memoization"
><h3
  >Higher-order memoization</h3
  ><p
  >Now higher-order memoization is easy. Apply yet another isomorphism, this time between functions and tries: The <code
    >trie</code
    > and <code
    >untrie</code
    > methods are exactly the mappings we need.</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="dt"
      >HasTrieIsomorph</span
      >( (<span class="dt"
      >HasTrie</span
      > a, <span class="dt"
      >HasTrie</span
      > (a &#8603; b))<br
       />               , a &#8594; b, a &#8603; b, trie, untrie)<br
       /></code
    ></pre
  ><p
  >So, to memoize a higher-order function <code
    >f &#8759; (a &#8594; b) &#8594; v</code
    >, we only a trie type for <code
    >a</code
    > and one for <code
    >a &#8603; b</code
    >. The latter (tries for trie-valued domains) are provided by the isomorphisms above, and additional ones.</p
  ></div
>

<div id="demo"
><h3
  >Demo</h3
  ><p
  >Our sample higher-order function will take a function of booleans and yield its value at <code
    >False</code
    > and at <code
    >True</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    >ft1 <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >Bool</span
      > &#8594; a) &#8594; [a]<br
       />ft1 f <span class="fu"
      >=</span
      > [f <span class="kw"
      >False</span
      >, f <span class="kw"
      >True</span
      >]<br
       /></code
    ></pre
  ><p
  >A sample input converts <code
    >False</code
    > to <code
    >0</code
    > and <code
    >True</code
    > to <code
    >1</code
    >:</p
  ><pre class="sourceCode haskell"
  ><code
    >f1 <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >Bool</span
      > &#8594; <span class="dt"
      >Int</span
      ><br
       />f1 <span class="kw"
      >False</span
      > <span class="fu"
      >=</span
      > <span class="dv"
      >0</span
      ><br
       />f1 <span class="kw"
      >True</span
      >  <span class="fu"
      >=</span
      > <span class="dv"
      >1</span
      ><br
       /></code
    ></pre
  ><p
  >A sample run without memoization:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="fu"
      >*</span
      ><span class="dt"
      >FunctorCombo.MemoTrie</span
      ><span class="fu"
      >&gt;</span
      > ft1 f1<br
       />[<span class="dv"
      >0</span
      >,<span class="dv"
      >1</span
      >]<br
       /></code
    ></pre
  ><p
  >and one with memoization:</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="fu"
      >*</span
      ><span class="dt"
      >FunctorCombo.MemoTrie</span
      ><span class="fu"
      >&gt;</span
      > memo ft1 f1<br
       />[<span class="dv"
      >0</span
      >,<span class="dv"
      >1</span
      >]<br
       /></code
    ></pre
  ><p
  >To illustrate what's going on behind the scenes, the following definitions (all of which type-check) progressively reveal the representation of the underlying memo trie. Most steps result from inlining a single <code
    >Trie</code
    > definition (as well as switching between <code
    >Trie k v</code
    > and the synonymous form <code
    >k &#8603; v</code
    >).</p
  ><pre class="sourceCode haskell"
  ><code
    >trie1a <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (<span class="dt"
      >Bool</span
      > &#8594; a) &#8603; (a, a)<br
       />trie1a <span class="fu"
      >=</span
      > trie ft1<br
       /><br
       />trie1b <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (<span class="dt"
      >Bool</span
      > &#8603; a) &#8603; (a, a)<br
       />trie1b <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1c <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (<span class="dt"
      >Either</span
      > () () &#8603; a) &#8603; (a, a)<br
       />trie1c <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1d <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; ((<span class="dt"
      >Trie</span
      > () &#215; <span class="dt"
      >Trie</span
      > ()) a) &#8603; (a, a)<br
       />trie1d <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1e <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (<span class="dt"
      >Trie</span
      > () a, <span class="dt"
      >Trie</span
      > () a) &#8603; (a, a)<br
       />trie1e <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1f <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (() &#8603; a, () &#8603; a) &#8603; (a, a)<br
       />trie1f <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1g <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (a, a) &#8603; (a, a)<br
       />trie1g <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1h <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; (<span class="dt"
      >Trie</span
      > a &#8728; <span class="dt"
      >Trie</span
      > a) (a, a)<br
       />trie1h <span class="fu"
      >=</span
      > trie1a<br
       /><br
       />trie1i <span class="dv"
      >&#8759;</span
      > <span class="dt"
      >HasTrie</span
      > a &#8658; a &#8603; a &#8603; (a, a)<br
       />trie1i <span class="fu"
      >=</span
      > unO trie1a<br
       /></code
    ></pre
  ></div
>

<div id="pragmatics"
><h3
  >Pragmatics</h3
  ><p
  >I'm happy with the correctness and elegance of the method in this post. It gives me the feeling of inevitable simplicity that I strive for -- obvious in hindsight. What about performance? After all, memoization is motivated by a desire to efficiency -- specifically, to reduce the cost of repeatedly applying the same function to the same argument, while keeping almost all of the modularity &amp; simplicity of a naïve algorithm.</p
  ><p
  >Memoization pays off when (a) a function is repeatedly applied to some arguments, and (b) when the cost of recomputing an application exceeds the cost of finding the previously computed result. (I'm over-simplifying here. Space efficiency matters also and can affect time efficiency.) The isomorphism technique used in this post and <a href="http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/" title="blog post"
    >a previous one</a
    > requires transforming an argument to the isomorphic type for each look-up and from the isomorphic type for each application. (I'm using &quot;isomorphic type&quot; to mean the type for which a <code
    >HasTrie</code
    > instance is already defined.) When these transformations are between function and trie form, I wonder how high the break-even threshold becomes.</p
  ><p
  >How might we avoid these transformations, thus reducing the overhead of memoizing?</p
  ><p
  >For conversion to isomorphic type during trie lookup, perhaps the cost could be reduced substantially through deforestation--inlining chains of <code
    >untrie</code
    > methods and applying optimizations to eliminate the many intermediate representation layers. GHC has gotten awfully good at this sort of thing. Maybe someone with more Haskell performance analysis &amp; optimization experience than I have would be interested in collaborating.</p
  ><p
  >For trie construction, I suspect the conversion <em
    >back</em
    > from the isomorphic type could be avoided by somehow holding onto the original form of the argument, before it was converted to the isomorphic type. I haven't attempted this idea yet.</p
  ><p
  >Another angle on reducing the cost of the isomorphism technique is to use memoization! After all, if memoizing is worthwhile at all, there will be repeated applications of the memoized function to the same arguments. Exactly in such a case, the conversion of arguments to isomorphic form will also be done repeatedly for these same arguments. When a conversion is both expensive and repeated, we'd like to memoize. I don't know how to get off the ground with this idea, however. If I'm trying to memoize a function of type <code
    >a &#8594; b</code
    >, then the required conversion has type <code
    >a &#8594; a'</code
    > for some type <code
    >a'</code
    > with a <code
    >HasTrie</code
    > instance. Memoizing that conversion is just as hard as memoizing the function we started with.</p
  ></div
>

<div id="conclusion"
><h3
  >Conclusion</h3
  ><p
  >Existing accounts of functional memoization I know of cover functions of the unit type, sums, and products, and they do so quite elegantly.</p
  ><p
  ><em
    >Type isomorphisms</em
    > form the consistent, central theme in this work. Functions from unit, sums and products have isomorphic forms with simpler domain types (and so on, recursively). Additional isomorphisms extend these fundamental building blocks to many other types, including integer types and algebraic data types. However, functions over function-valued domains are conspicuously missing (though I hadn't noticed until recently). This post fills that gap neatly, using yet another isomorphism, and moreover an isomorphism that has been staring us in the face all along: the one between functions and tries.</p
  ><p
  >I wonder:</p
  ><ul
  ><li
    >Given how this trick shouts to be noticed, has it been discovered and written up?</li
    ><li
    >How useful will higher-order memoization turn out to be?</li
    ><li
    >How efficient is the straightforward implementation given above?</li
    ><li
    >Can the conversions between isomorphic domain types be done inexpensively, perhaps eliminating many altogether?</li
    ><li
    >How does [non-strict memoization][] fit in with higher-order memoization?</li
    ></ul
  ></div
>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=119&amp;md5=38c9c222d1cfdb6bbafc4cb90a393efa"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/memoizing-higher-order-functions/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fmemoizing-higher-order-functions&amp;language=en_GB&amp;category=text&amp;title=Memoizing+higher-order+functions&amp;description=Memoization+incrementally+converts+functions+into+data+structures.+It+pays+off+when+a+function+is+repeatedly+applied+to+the+same+arguments+and+applying+the+function+is+more+expensive+than+accessing+the...&amp;tags=isomorphism%2Cmemoization%2Ctrie%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Elegant memoization with higher-order types</title>
		<link>http://conal.net/blog/posts/elegant-memoization-with-higher-order-types</link>
		<comments>http://conal.net/blog/posts/elegant-memoization-with-higher-order-types#comments</comments>
		<pubDate>Wed, 21 Jul 2010 04:48:22 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[functor]]></category>
		<category><![CDATA[isomorphism]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=117</guid>
		<description><![CDATA[A while back, I got interested in functional memoization, especially after seeing some code from Spencer Janssen using the essential idea of Ralf Hinze&#8217;s paper Generalizing Generalized Tries. The blog post Elegant memoization with functional memo tries describes a library, MemoTrie, based on both of these sources, and using associated data types. I would have [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Elegant memoization with higher-order types

Tags: functor, memoization, isomorphism, trie

URL: http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/

-->

<!-- references -->

<!-- teaser -->

<p>A while back, I got interested in functional memoization, especially after seeing some code from Spencer Janssen using the essential idea of Ralf Hinze&#8217;s paper <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.8.4069" title="Paper: &quot;Generalizing Generalized Tries&quot; by Ralf Hinze">Generalizing Generalized Tries</a></em>.
The blog post <em><a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">Elegant memoization with functional memo tries</a></em> describes a library, <a href="http://haskell.org/haskellwiki/MemoTrie" title="Haskell wiki page for the MemoTrie library">MemoTrie</a>, based on both of these sources, and using <a href="http://www.cse.unsw.edu.au/~chak/papers/papers.html#assoc" title="Paper: &quot;Associated Types with Class&quot;">associated data types</a>.
I would have rather used associated type synonyms and standard types, but I couldn&#8217;t see how to get the details to work out.
Recently, while playing with functor combinators, I realized that they might work for memoization, which they do quite nicely.</p>

<p>This blog post shows how functor combinators lead to an even more elegant formulation of functional memoization.
The code is available as part of the <a href="http://hackage.haskell.org/package/functor-combo" title="Hackage entry: functor-combo">functor-combo</a> package.</p>

<p>The techniques in this post are not so much new as they are ones that have recently been sinking in for me.
See <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.8.4069" title="Paper: &quot;Generalizing Generalized Tries&quot; by Ralf Hinze">Generalizing Generalized Tries</a></em>, as well as <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.140.2412" title="Paper by Alexey Rodriguez, Stefan Holdermans, Andres Löh, and Johan Jeuring">Generic programming with fixed points for mutually recursive datatypes</a></em>.</p>

<p><strong>Edits</strong>:</p>

<ul>
<li>2011-01-28: Fixed small typo: &#8220;<em>b^^a^^</em>&#8221; ⟼ &#8220;<em>b<sup>a</sup></em>&#8220;</li>
<li>2010-09-10: Corrected <code>Const</code> definition to use <code>newtype</code> instead of <code>data</code>.</li>
<li>2010-09-10: Added missing <code>Unit</code> type definition (as <code>Const ()</code>).</li>
</ul>

<!-- without a comment or something here, the last item above becomes a paragraph -->

<p><span id="more-117"></span></p>

<h3>Tries as associated data type</h3>

<p>The <a href="http://haskell.org/haskellwiki/MemoTrie" title="Haskell wiki page for the MemoTrie library">MemoTrie</a> library is centered on a class <code>HasTrie</code> with an associated data type of tries (efficient indexing structures for memoized functions):</p>

<pre><code>class HasTrie k where
    data (:→:) k :: * → *
    trie   :: (k  →  v) → (k :→: v)
    untrie :: (k :→: v) → (k  →  v)
</code></pre>

<p>The type <code>a :→: b</code> represents a trie that maps values of type <code>a</code> to values of type <code>b</code>.
The trie representation depends only on <code>a</code>.</p>

<p>Memoization is a simple combination of these two methods:</p>

<pre><code>memo :: HasTrie a ⇒ (a → b) → (a → b)
memo = untrie . trie
</code></pre>

<p>The <code>HasTrie</code> instance definitions correspond to isomorphisms invoving function types.
The isomorphisms correspond to the familiar rules of exponents, if we translate <em>a → b</em> into <em>b<sup>a</sup></em>.
(See <em><a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">Elegant memoization with functional memo tries</a></em> for more explanation.)</p>

<pre><code>instance HasTrie () where
    data () :→: x = UnitTrie x
    trie f = UnitTrie (f ())
    untrie (UnitTrie x) = const x

instance (HasTrie a, HasTrie b) ⇒ HasTrie (Either a b) where
    data (Either a b) :→: x = EitherTrie (a :→: x) (b :→: x)
    trie f = EitherTrie (trie (f . Left)) (trie (f . Right))
    untrie (EitherTrie s t) = either (untrie s) (untrie t)

instance (HasTrie a, HasTrie b) ⇒ HasTrie (a,b) where
    data (a,b) :→: x = PairTrie (a :→: (b :→: x))
    trie f = PairTrie (trie (trie . curry f))
    untrie (PairTrie t) = uncurry (untrie .  untrie t)
</code></pre>

<h3>Functors and functor combinators</h3>

<p>For notational convenience, let &#8220;<code>(:→:)</code>&#8221; be a synonym for &#8220;<code>Trie</code>&#8220;:</p>

<pre><code>type k :→: v = Trie k v
</code></pre>

<p>And replace the associated <code>data</code> with an associated <code>type</code>.</p>

<pre><code>class HasTrie k where
    type Trie k :: * → *
    trie   :: (k  →  v) → (k :→: v)
    untrie :: (k :→: v) → (k  →  v)
</code></pre>

<p>Then, imitating the three <code>HasTrie</code> instances above,</p>

<pre><code>type Trie () v = v

type Trie (Either a b) v = (Trie a v, Trie b v)

type Trie (a,b) v = Trie a (Trie b v)
</code></pre>

<p>Imagine that we have type lambdas for writing higher-kinded types.</p>

<pre><code>type Trie () = λ v → v

type Trie (Either a b) = λ v → (Trie a v, Trie b v)

type Trie (a,b) = λ v → Trie a (Trie b v)
</code></pre>

<p>Type lambdas are often written as &#8220;Λ&#8221; (capital &#8220;λ&#8221;) instead.
In the land of values, these three right-hand sides correspond to common building blocks for functions, namely identity, product, and composition:</p>

<pre><code>id      = λ v → v
f *** g = λ v → (f v, g v)
g  .  f = λ v → g (f v)
</code></pre>

<p>These building blocks arise in the land of types.</p>

<pre><code>newtype Id a = Id a

data (f :*: g) a = f a :*: g a

newtype (g :. f) a = O (g (f a))
</code></pre>

<p>where <code>Id</code>, <code>f</code> and <code>g</code> are functors.
Sum and a constant functor are also common building blocks:</p>

<pre><code>data (f :+: g) a = InL (f a) | InR (g a)

newtype Const x a = Const x

type Unit = Const () -- one non-⊥ inhabitant
</code></pre>

<h3>Tries as associated type synonym</h3>

<p>Given these standard definitions, we can eliminate the special-purpose data types used, replacing them with our standard functor combinators:</p>

<pre><code>instance HasTrie () where
  type Trie ()  = Id
  trie   f      = Id (f ())
  untrie (Id v) = const v

instance (HasTrie a, HasTrie b) =&gt; HasTrie (Either a b) where
  type Trie (Either a b) = Trie a :*: Trie b
  trie   f           = trie (f . Left) :*: trie (f . Right)
  untrie (ta :*: tb) = untrie ta `either` untrie tb

instance (HasTrie a, HasTrie b) ⇒ HasTrie (a , b) where
  type Trie (a , b) = Trie a :. Trie b
  trie   f      = O (trie (trie . curry f))
  untrie (O tt) = uncurry (untrie . untrie tt)
</code></pre>

<p>At first blush, it might appear that we&#8217;ve simply moved the data type definitions outside of the instances.
However, the extracted functor combinators have other uses, as explored in polytypic programming.
I&#8217;ll point out some of these uses in the next few blog posts.</p>

<h3>Isomorphisms</h3>

<p>Many types are isomorphic variations, and so their corresponding tries can share a common representation.
For instance, triples are isomorphic to nested pairs:</p>

<pre><code>detrip :: (a,b,c) → ((a,b),c)
detrip (a,b,c) = ((a,b),c)

trip :: ((a,b),c) → (a,b,c)
trip ((a,b),c) = (a,b,c)
</code></pre>

<p>A trie for triples can be a a trie for pairs (already defined).
The <code>trie</code> and <code>untrie</code> methods then just perform conversions around the corresponding methods on pairs:</p>

<pre><code>instance (HasTrie a, HasTrie b, HasTrie c) ⇒ HasTrie (a,b,c) where
    type Trie (a,b,c) = Trie ((a,b),c)
    trie f = trie (f . trip)
    untrie t = untrie t . detrip
</code></pre>

<p>All type isomorphisms can use this same pattern.
I don&#8217;t think Haskell is sufficiently expressive to capture this pattern within the language, so I&#8217;ll resort to a C macro.
There are five parameters:</p>

<ul>
<li><code>Context</code>: the instance context;</li>
<li><code>Type</code>: the type whose instance is being defined;</li>
<li><code>IsoType</code>: the isomorphic type;</li>
<li><code>toIso</code>: conversion function <em>to</em> <code>IsoType</code>; and</li>
<li><code>fromIso</code>: conversion function <em>from</em> <code>IsoType</code>.</li>
</ul>

<p>The macro:</p>

<pre><code>#define HasTrieIsomorph(Context,Type,IsoType,toIso,fromIso)  
instance Context ⇒ HasTrie (Type) where {  
  type Trie (Type) = Trie (IsoType);  
  trie f = trie (f . (fromIso));  
  untrie t = untrie t . (toIso);  
}
</code></pre>

<p>Now we can easily define <code>HasTrie</code> instances:</p>

<pre><code>HasTrieIsomorph( (), Bool, Either () ()
               ,  c -&gt; if c then Left () else Right ()
               , either ( () -&gt; True) ( () -&gt; False))

HasTrieIsomorph( (HasTrie a, HasTrie b, HasTrie c), (a,b,c), ((a,b),c)
               , λ (a,b,c) → ((a,b),c), λ ((a,b),c) → (a,b,c))

HasTrieIsomorph( (HasTrie a, HasTrie b, HasTrie c, HasTrie d)
               , (a,b,c,d), ((a,b,c),d)
               , λ (a,b,c,d) → ((a,b,c),d), λ ((a,b,c),d) → (a,b,c,d))
</code></pre>

<p>In most (but not all) cases, the first argument (<code>Context</code>) could simply be that the isomorphic type <code>HasTrie</code>, e.g.,</p>

<pre><code>HasTrieIsomorph( HasTrie ((a,b),c), (a,b,c), ((a,b),c)
               , λ (a,b,c) → ((a,b),c), λ ((a,b),c) → (a,b,c))
</code></pre>

<p>We could define another macro that captures this pattern and requires one fewer argument.
On the other hand, there is merit to keeping the contextual requirements explicit.</p>

<h3>Regular data types</h3>

<p>A regular data type is one in which the recursive uses are at the same type.
Functions over such types are often defined via <em>monomorphic</em> recursion.
Data types that do not satisfy this constraint are called &#8220;<a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.31.3551" title="Paper by Richard Bird and Lambert Meertens">nested</a>&#8220;.</p>

<p>As in several recent generic programming systems, regular data types can be encoded generically through a type class that unwraps one level of functor from a type.
The regular data type is the fixpoint of that functor.
See, e.g., <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.6390" title="Paper by Ulf Norell and Patrik Jansson">Polytypic programming in Haskell</a></em>.
Adopting the style of <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.142.4778" title="Paper by Thomas Noort, Alexey Rodriguez, Stefan Holdermans, Johan Jeuring, Bastiaan Heeren">A Lightweight Approach to Datatype-Generic Rewriting</a></em>,</p>

<pre><code>class Functor (PF t) ⇒ Regular t where
  type PF t :: * → *
  wrap   :: PF t t → t
  unwrap :: t → PF t t
</code></pre>

<p>Here &#8220;<code>PF</code>&#8221; stands for &#8220;pattern functor&#8221;.</p>

<p>The pattern functors can be constructed out of the functor combinators above.
For instance, a list at the top level is either empty or a value and a list.
Translating this description:</p>

<pre><code>instance Regular [a] where
  type PF [a] = Unit :+: Const a :*: Id

  unwrap []     = InL (Const ())
  unwrap (a:as) = InR (Const a :*: Id as)

  wrap (InL (Const ()))          = []
  wrap (InR (Const a :*: Id as)) = a:as
</code></pre>

<p>As another example, consider rose trees ([<code>Data.Tree</code>][]):</p>

<pre><code>data Tree  a = Node a [Tree a]

instance Regular (Tree a) where

  type PF (Tree a) = Const a :*: []

  unwrap (Node a ts) = Const a :*: ts

  wrap (Const a :*: ts) = Node a ts
</code></pre>

<p>Regular types allow for even more succinct <code>HasTrie</code> instance implementations.
Specialize <code>HasTrieIsomorph</code> further:</p>

<pre><code>#define HasTrieRegular(Context,Type)  
HasTrieIsomorph(Context, Type, PF (Type) (Type) , unwrap, wrap)
</code></pre>

<p>For instance, for lists and rose trees:</p>

<pre><code>HasTrieRegular(HasTrie a, [a])
HasTrieRegular(HasTrie a, Tree a)
</code></pre>

<p>The <code>HasTrieRegular</code> macro could be specialized even further for single-parameter polymorphic data types:</p>

<pre><code>#define HasTrieRegular1(TypeCon) HasTrieRegular(HasTrie a, TypeCon a)

HasTrieRegular1([])
HasTrieRegular1(Tree)
</code></pre>

<p>You might wonder if I&#8217;m cheating here, by claiming very simple trie specifications when I&#8217;m really just shuffling code around.
After all, the complexity removed from <code>HasTrie</code> instances shows up in <code>Regular</code> instances.
The win in making this shuffle is that <code>Regular</code> is handy for other purposes, as illustrated in <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.140.2412" title="Paper by Alexey Rodriguez, Stefan Holdermans, Andres Löh, and Johan Jeuring">Generic programming with fixed points for mutually recursive datatypes</a></em> (including <code>fold</code>, <code>unfold</code>, and <code>fmap</code>).
(More examples in <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.142.4778" title="Paper by Thomas Noort, Alexey Rodriguez, Stefan Holdermans, Johan Jeuring, Bastiaan Heeren">A Lightweight Approach to Datatype-Generic Rewriting</a></em>.)</p>

<h3>Trouble</h3>

<p>Sadly, these elegant trie definitions have a problem.
Trying to compile them leads to a error message from GHC.
For instance,</p>

<pre><code>Nested type family application
  in the type family application: Trie (PF [a] [a])
(Use -XUndecidableInstances to permit this)
</code></pre>

<p>Adding <code>UndecidableInstances</code> silences this error message, but leads to nontermination in the compiler.</p>

<p>Expanding definitions, I can see the likely cause of nontermination.
The definition in terms of a type family allows an infinite type to sneak through, and I guess GHC&#8217;s type checker is unfolding infinitely.</p>

<p>As a simpler example:</p>

<pre><code>{-# LANGUAGE TypeFamilies, UndecidableInstances #-}

type family List a :: *

type instance List a = Either () (a, List a)

-- Hangs ghc 6.12.1:
nil :: List a
nil = Left ()
</code></pre>

<h3>A solution</h3>

<p>Since GHC&#8217;s type-checker cannot handle directly recursive types, perhaps we can use a standard avoidance strategy, namely introducing a <code>newtype</code> or <code>data</code> definition to break the cycle.
For instance, as a trie for <code>[a]</code>, we got into trouble by using the trie of the unwrapped form of <code>[a]</code>, i.e., <code>Trie (PF [a] [a])</code>.
So instead,</p>

<pre><code>newtype ListTrie a v = ListTrie (Trie (PF [a] [a]) v)
</code></pre>

<p>which is to say</p>

<pre><code>newtype ListTrie a v = ListTrie (PF [a] [a] :→: v)
</code></pre>

<p>Now <code>wrap</code> and <code>unwrap</code> as before, and add &amp; remove <code>ListTrie</code> as needed:</p>

<pre><code>instance HasTrie a ⇒ HasTrie [a] where
  type Trie [a] = ListTrie a
  trie f = ListTrie (trie (f . wrap))
  untrie (ListTrie t) = untrie t . unwrap
</code></pre>

<p>Again, abstract the boilerplate code into a C macro:</p>

<pre><code>#define HasTrieRegular(Context,Type,TrieType,TrieCon) 
newtype TrieType v = TrieCon (PF (Type) (Type) :→: v); 
instance Context ⇒ HasTrie (Type) where { 
  type Trie (Type) = TrieType; 
  trie f = TrieCon (trie (f . wrap)); 
  untrie (TrieCon t) = untrie t . unwrap; 
}
</code></pre>

<p>For instance,</p>

<pre><code>HasTrieRegular(HasTrie a, [a] , ListTrie a, ListTrie)
HasTrieRegular(HasTrie a, Tree, TreeTrie a, TreeTrie)
</code></pre>

<p>Again, simplify a bit with a specialization to unary regular types:</p>

<pre><code>#define HasTrieRegular1(TypeCon,TrieCon) 
HasTrieRegular(HasTrie a, TypeCon a, TrieCon a, TrieCon)
</code></pre>

<p>And then use the following declarations instead:</p>

<pre><code>HasTrieRegular1([]  , ListTrie)
HasTrieRegular1(Tree, TreeTrie)
</code></pre>

<p>Similarly for binary etc as needed.</p>

<p>The second macro parameter (<code>TrieCon</code>) is just a name, which I don&#8217;t to be used other than in the macro-generated code.
It could be eliminated, if there were a way to gensym the name.
Perhaps with Template Haskell?</p>

<h3>Conclusion</h3>

<p>I like the elegance of constructing memo tries in terms of common functor combinators.
Standard pattern functors allow for extremely succinct trie specifications for regular data types.
However, these specifications lead to nontermination of the type checker, which can then be avoided by the standard trick of introducing a newtype to break type recursion.
As often, this trick brings introduces some clumsiness.
Perhaps the problem can also be avoided by using a formulation using <em>bifunctors</em>, as in <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.60.5251" title="Paper by Jeremy Gibbons">Design Patterns as Higher-Order Datatype-Generic Programs</a></em> and <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.6390" title="Paper by Ulf Norell and Patrik Jansson">Polytypic programming in Haskell</a></em>, which allows the fixed-point nature of regular data types to be exposed.</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=117&amp;md5=32e9a954390ac5cfba0f5fb929af467d"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/elegant-memoization-with-higher-order-types/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Felegant-memoization-with-higher-order-types&amp;language=en_GB&amp;category=text&amp;title=Elegant+memoization+with+higher-order+types&amp;description=A+while+back%2C+I+got+interested+in+functional+memoization%2C+especially+after+seeing+some+code+from+Spencer+Janssen+using+the+essential+idea+of+Ralf+Hinze%26%238217%3Bs+paper+Generalizing+Generalized+Tries.+The+blog...&amp;tags=functor%2Cisomorphism%2Cmemoization%2Ctrie%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Non-strict memoization</title>
		<link>http://conal.net/blog/posts/nonstrict-memoization</link>
		<comments>http://conal.net/blog/posts/nonstrict-memoization#comments</comments>
		<pubDate>Wed, 14 Jul 2010 02:46:23 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[unamb]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=110</guid>
		<description><![CDATA[I&#8217;ve written a few posts about functional memoization. In one of them, Luke Palmer commented that the memoization methods are correct only for strict functions, which I had not noticed before. In this note, I correct this flaw, extending correct memoization to non-strict functions as well. The semantic notion of least upper bound (which can [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Non-strict memoization

Tags: unamb, memoization, trie

URL: http://conal.net/blog/posts/nonstrict-memoization/

-->

<!-- references -->

<!-- teaser -->

<p>I&#8217;ve written a few <a href="http://conal.net/blog/tag/memoization/" title="Posts on memoization">posts about functional memoization</a>.
In <a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">one of them</a>, <a href="http://lukepalmer.wordpress.com/" title="Luke Palmer's blog">Luke Palmer</a> commented that the memoization methods are correct only for strict functions, which I had not noticed before.
In this note, I correct this flaw, extending correct memoization to non-strict functions as well.
The semantic notion of <a href="http://conal.net/blog/tag/unamb/" title="Posts on unambiguous choice and least upper bound"><em>least upper bound</em> (which can be built of <em>unambiguous choice</em>)</a> plays a crucial role.</p>

<p><strong>Edits</strong>:</p>

<ul>
<li>2010-07-13: Fixed the non-strict memoization example to use an argument of <code>undefined</code> (⊥) as intended.</li>
<li>2010-07-23: Changed spelling from &#8220;nonstrict&#8221; to the much more popular &#8220;non-strict&#8221;.</li>
<li>2011-02-16: Fixed minor typo. (&#8220;constraint on result&#8221; → &#8220;constraint on the result type&#8221;)</li>
</ul>

<!-- without a comment or something here, the last item above becomes a paragraph -->

<p><span id="more-110"></span></p>

<h3>What is memoization?</h3>

<p>In purely functional programming, applying a function to equal arguments gives equal results.
However, the second application is as costly as the first one.
The idea of memoization, invented by Donald Michie in the 1960s, is to cache the results of applications and reuse those results in subsequent applications.
Memoization is a handy technique to know, as it can dramatically reduces expense while making little impact on an algorithm&#8217;s simplicity.</p>

<p>Early implementations of memoization were imperative.
Some sort of table (e.g., a hash table) is initialized as empty.
Whenever the memoized function is applied, the argument is looked up in the table.
If present, the corresponding result is returned.
Otherwise, the original function is applied to the argument, and the result is stored in the table, keyed by the argument.</p>

<h3>Functional memoization</h3>

<p>Can memoization be implemented functionally (without assignment)?
One might argue that it cannot, considering that we want the table structure to get filled in destructively, as the memoized function is sampled.</p>

<p>However, this argument is flawed (like many informal arguments of impossibility).
Although we want a mutation to happen, we needn&#8217;t ask for one explicitly.
Instead, we can exploit the mutation that happens <em>inside the implementation</em> of laziness.</p>

<p>For instance, consider memoizing a function of booleans:</p>

<pre><code>memoBool :: (Bool -&gt; b) -&gt; (Bool -&gt; b)
</code></pre>

<p>In this case, the &#8220;table&#8221; can simply be a pair, with one slot for the argument <code>False</code> and one for <code>True:</code></p>

<pre><code>type BoolTable a = (a,a)

memoBool f = lookupBool (f False, f True)

lookupBool :: BoolTable b -&gt; Bool -&gt; b
lookupBool (f,_) False = f
lookupBool (_,t) True  = t
</code></pre>

<p>For instance, consider this simple function and a memoized version:</p>

<pre><code>f1 b = if b then 3 else 4

s1 = memoBool f1
</code></pre>

<p>The memo table will be <code>(f False, f True)</code>, i.e., <code>(4,3)</code>.
Checking that <code>s1</code> is equivalent to <code>f1</code>:</p>

<pre><code>s1 False ≡ lookupBool (4,3) False ≡ 4 ≡ f1 False
s1 True  ≡ lookupBool (4,3) True  ≡ 3 ≡ f1 True
</code></pre>

<p>Other argument types have other table representations, and these table types can be defined <a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">systematically and elegantly</a>.</p>

<p>Now, wait a minute!
Building an entire table up-front doesn&#8217;t sound like the incremental algorithm Richie invented, especially considering that the domain type can be quite large and even infinite.
However, in a <em>lazy</em> language, incremental construction of data structures is automatic and pervasive, and infinite data structures are bread &amp; butter.
So the computing and updating doesn&#8217;t have to be <em>expressed</em> imperatively.</p>

<p>While lazy construction can be helpful for pairs, it&#8217;s <em>essential</em> for infinite tables, as needed for domain types that are enornmously large (e.g., <code>Int</code>), and even infinitely large (e.g., <code>Integer</code>, or <code>[Bool]</code>).
However, laziness brings to memoization not only a gift, but also a difficulty, namely the challenge of correctly memoizing <em>non-strict</em> functions, as we&#8217;ll see next.</p>

<h3>A problem with memoizing non-strict functions</h3>

<p>The confirmation above that <code>s1 ≡ f1</code> has a mistake: it fails to consider a third possible choice of argument, namely ⊥.
Let&#8217;s check this case now:</p>

<pre><code>s1 ⊥ ≡ lookupBool (4,3) ⊥ ≡ ⊥ ≡ f1 ⊥
</code></pre>

<p>The ⊥ case does not show up explicitly in the definition of <code>lookupBool</code>, but is implied by the use of pattern-matching against <code>True</code> and <code>False</code>.
For the same reason (in the definition of <code>if-then-else</code>), <code>f1 ⊥ ≡ ⊥</code>, so indeed <code>s1 ≡ f1</code>.
The key saving grace here is that <code>f1</code> is already strict, so the strictness introduced by <code>lookupBool</code> is harmless.</p>

<p>To see how memoization add strictness, consider a memoizing a <em>non-strict</em> function of booleans:</p>

<pre><code>f2 b = 5

s2 = memoBool f2
</code></pre>

<p>The memo table will be <code>(f False, f True)</code>, i.e., <code>(5,5)</code>.
Checking that <code>s2</code> is equivalent to <code>f2</code>:</p>

<pre><code>s2 False ≡ lookupBool (5,5) False ≡ 5 ≡ f2 False
s2 True  ≡ lookupBool (5,5) True  ≡ 5 ≡ f2 True
</code></pre>

<p>However,</p>

<pre><code>s2 ⊥ ≡ lookupBool (5,5) ⊥ ≡ ⊥
</code></pre>

<p>The latter equality is due again to pattern matching against <code>False</code> and <code>True</code> in <code>lookupBool</code>.</p>

<p>In contrast, <code>f2 ⊥ ≡ 5</code>, so <code>s2 ≢ f2</code>, so <code>memoBool</code> does not correctly memoize.</p>

<h3>Non-strict memoization</h3>

<p>The bug in <code>memoBool</code> comes from ignoring one of the possible boolean values.
In a lazy language, <code>Bool</code> has three possible values, not two.
A simple solution then might be for the memo table to be a triple instead of a pair:</p>

<pre><code>type BoolTable a = (a,a,a)

memoBool h = lookupBool (h ⊥, h False, h True)
</code></pre>

<p>Table lookup needs one additional case:</p>

<pre><code>lookupBool :: BoolTable a -&gt; Bool -&gt; a
lookupBool (b,_,_) ⊥     = b
lookupBool (_,f,_) False = f
lookupBool (_,_,t) True  = t
</code></pre>

<p>I hope you read my posts with a good deal of open-mindedness, but also with some skepticism.
This revised definition of <code>lookupBool</code> is not legitimate Haskell code, and for a good reason.
If we could write and run this kind of code, we could solve the halting problem:</p>

<pre><code>halts :: a -&gt; Bool
halts ⊥ = False
halts _ = True
</code></pre>

<p>The problem here is not just that ⊥ is not a legitimate Haskell <em>pattern</em>, but more fundamentally that equality with ⊥ is non-computable.</p>

<p>The revised <code>lookupBool</code> function and the <code>halts</code> function violate a fundamental semantic property, namely <em>monotonicity</em>  (of information content).
Monotonicity of a function <code>h</code> means that</p>

<pre><code>∀ a b. a ⊑ b ⟹ h a ⊑ h b
</code></pre>

<p>where &#8220;⊑&#8221; means has less (or equal) information content, as explained in <em><a href="http://conal.net/blog/posts/merging-partial-values/" title="blog post">Merging partial values</a></em>.
In other words, if you tell <code>f</code> more about an argument, it will tell you more about the result, where &#8220;more&#8221; (really more-or-equal) includes compatibility (no contradiction of previous knowledge).</p>

<p>The <code>halts</code> function is nonmonotonic, since, for instance, <code>⊥ ⊑ 3</code>, and <code>h ⊥ ≡ False</code> and <code>h 3 ≡ True</code>, but <code>False ⋢ True</code>.
(<code>False</code> and <code>True</code> are incompatible, i.e., they contradict each other.)</p>

<p>Similarly, the function <code>lookupBool (5,3,4)</code> is nonmonotonic, which you can verify by applying it to ⊥ and to <code>False</code>.
Although <code>⊥ ⊑ False</code>, <code>h ⊥ ≡ 5</code> and <code>h False ≡ 3</code>, but <code>5 ⋢ 3</code>.
Similarly, <code>⊥ ⊑ True</code>, <code>h ⊥ ≡ 5</code> and <code>h True ≡ 5</code>, but <code>5 ⋢ 4</code>.</p>

<p>So this particular memo table gets us into trouble (nonmonotonicity).
Are there other memo tables <code>(b,f,t)</code> that lead to monotonic lookup?
Re-examining the breakdown shows us a necessary and sufficient condition, which is that <code>b ⊑ f</code> and <code>b ⊑ t</code>.</p>

<p>Look again at the particular use of <code>lookupBool</code> in the definition of <code>memoBool</code> above, and you&#8217;ll see that</p>

<pre><code>b ≡ h ⊥
f ≡ h False
t ≡ h True
</code></pre>

<p>so the monotonicity condition becomes <code>h ⊥ ⊑ h False</code> and <code>h ⊥ ⊑ h True</code>.
This condition holds, thanks to the monotonicity of all computable functions <code>h</code>.</p>

<p>So the triple-based <code>lookupBool</code> can be semantically problematic outside of its motivating context, but never as used in <code>memoBool</code>.
That is, the triple-based definition of <code>memoBool</code> correctly specifies the (computable) meaning we want, but isn&#8217;t an implementation.
How might we correctly implement <code>memoBool</code>?</p>

<p>In <em><a href="http://conal.net/blog/posts/lazier-function-definitions-by-merging-partial-values/" title="blog post">Lazier function definitions by merging partial values</a></em>, I examined the standard Haskell style (inherited from predecessors) of definition by clauses, pointing out how that style is teasingly close to a declarative reading in which each clause is a true equation (possibly conditional).
I transformed the standard style into a form with modular, declarative semantics.</p>

<p>Let&#8217;s try transforming <code>lookupBool</code> into this modular form:</p>

<pre><code>lookupBool :: BoolTable a -&gt; Bool -&gt; a
lookupBool (b,f,t) = (λ ⊥ → b) ⊔ (λ False → f) ⊔ (λ True → t)
</code></pre>

<p>We still have the problem with <code>λ ⊥ → b</code> (nonmonotonicity), but it&#8217;s now isolated.
What if we broaden the domain from just ⊥ (for which we cannot dependably test) to <em>all</em> arguments, i.e., <code>λ _ → b</code> (i.e., <code>const b</code>)?
This latter function is the least one (in the information ordering) that is monotonic and contains all the information present in <code>λ ⊥ → b</code>.
(Exercise: prove.)
Dissecting this function:</p>

<pre><code>const b ≡ (λ _ → b) ≡ (λ ⊥ → b) ⊔ (λ False → b) ⊔ (λ True → b)
</code></pre>

<p>So</p>

<pre><code>  const b ⊔ (λ False → f) ⊔ (λ True → t)
≡ (λ ⊥ → b) ⊔ (λ False → b) ⊔ (λ True → b) ⊔ (λ False → f) ⊔ (λ True → t)
≡ (λ ⊥ → b) ⊔ (λ False → b) ⊔ (λ False → f) ⊔ (λ True → b) ⊔ (λ True → t)
≡ (λ ⊥ → b) ⊔ (λ False → (b ⊔ f)) ⊔ (λ True → (b ⊔ t))
≡ (λ ⊥ → b) ⊔ (λ False →      f ) ⊔ (λ True →      t )
</code></pre>

<p>under the condition that <code>b ⊑ f</code> and <code>b ⊑ t</code>, which does hold in the context of our use (again by monotonicity of the <code>h</code> in <code>memoBool</code>).
Therefore, in this context, we can replace the nonmonotonic <code>λ ⊥ → b</code> with the monotonic <code>const b</code>, while preserving the meaning of <code>memoBool</code>.</p>

<p>Behind the dancing symbols in the proof above lies the insight that we can use the ⊥ case even for non-⊥ arguments, because the result will be subsumed by non-⊥ cases, thanks to the lubs (⊔).</p>

<p>The original two non-⊥ cases can be combined back into their more standard (less modular) Haskell form, and we can revert to our original strict table and lookup function.
Our use of ⊔ requires the result type to be ⊔-able.</p>

<pre><code>memoBool :: HasLub b =&gt; (Bool -&gt; b) -&gt; (Bool -&gt; b)

type BoolTable a = (a,a)

memoBool h = const (h ⊥) ⊔ lookupBool (h False, h True)

lookupBool :: BoolTable b -&gt; Bool -&gt; b
lookupBool (f,_) False = f
lookupBool (_,t) True  = t
</code></pre>

<p>So the differences between our original, too-strict <code>memoBool</code> and this correct one are quite small: the <code>HasLub</code> constraint and the &#8220;<code>const (f ⊥) ⊔</code>&#8220;.</p>

<p>The <code>HasLub</code> constraint on the result type warns us of a possible loss of generality.
Are there types for which we do not know how to ⊔?
Primitive types are flat, where ⊔ is equivalent to <code>unamb</code>; and there are <code>HasLub</code> instances for functions, sums, and products.
(See <em><a href="http://conal.net/blog/posts/merging-partial-values/" title="blog post">Merging partial values</a></em>.)
<code>HasLub</code> could be derived automatically for algebraic data types (labeled sums of products) and trivially for <code>newtype</code>.
Perhaps abstract types need some extra thought.</p>

<h3>Demo</h3>

<p>First, import the <a href="http://haskell.org/haskellwiki/lub" title="wiki page">lub</a> package:</p>

<pre><code>{-# LANGUAGE Rank2Types #-}
{-# OPTIONS -Wall #-}
import Data.Lub
</code></pre>

<p>And define a type of <em>strict</em> memoization.
Borrowing from <a href="http://lukepalmer.wordpress.com/" title="Luke Palmer's blog">Luke Palmer</a>&#8216;s <a href="http://lukepalmer.wordpress.com/2008/10/14/data-memocombinators/" title="blog post by Luke Palmer">MemoCombinators</a> package, define a type of strict memoizers:</p>

<pre><code>type MemoStrict a = forall r. (a -&gt; r) -&gt; (a -&gt; r)
</code></pre>

<p>Now a strict memoizer for <code>Bool</code>, as above:</p>

<pre><code>memoBoolStrict :: MemoStrict Bool
memoBoolStrict h = lookupBool (h False, h True)
 where
   lookupBool (f,_) False = f
   lookupBool (_,t) True  = t
</code></pre>

<p>Test out the strict memoizer.
First on a strict function:</p>

<pre><code>h1, s1 :: Bool -&gt; Integer
h1 =  b -&gt; if b then 3 else 4
s1 = memoBoolStrict h1
</code></pre>

<p>A test run:</p>

<pre><code>*Main&gt; h1 True
3
*Main&gt; s1 True
3
</code></pre>

<p>Next on a non-strict function:</p>

<pre><code>h2, s2 :: Bool -&gt; Integer
h2 = const 5
s2 = memoBoolStrict h2
</code></pre>

<p>And test:</p>

<pre><code>*Main&gt; h2 undefined
5
*Main&gt; s2 undefined
*** Exception: Prelude.undefined
</code></pre>

<p>Now define a type of non-strict memoizers:</p>

<pre><code>type Memo a = forall r. HasLub r =&gt; (a -&gt; r) -&gt; (a -&gt; r)
</code></pre>

<p>And a non-strict <code>Bool</code> memoizer:</p>

<pre><code>memoBool :: Memo Bool
memoBool h = const (h undefined) `lub` memoBoolStrict h
</code></pre>

<p>Testing:</p>

<pre><code>*Main&gt; h2 undefined
5
*Main&gt; n2 undefined
5
</code></pre>

<p>Success!</p>

<h3>Beyond <code>Bool</code></h3>

<p>To determine how to generalize <code>memoBool</code> to types other than <code>Bool</code>, consider what properties of <code>Bool</code> mattered in our development.</p>

<ul>
<li>We know how to strictly memoize over <code>Bool</code> (i.e., what shape to use for the memo table and how to fill it).</li>
<li><code>Bool</code> is flat.</li>
</ul>

<p>The first condition also holds (<a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">elegantly</a>) for integral types, sums, products, and algebraic types.</p>

<p>The second condition is terribly restrictive and fails to hold for sums, products and most algebraic types (e.g., <code>Maybe</code> and <code>[]</code>).</p>

<p>Consider a Haskell function <code>h :: (a,b) -&gt; c</code>.
An element of type <code>(a,b)</code> is either <code>⊥</code> or <code>(x,y)</code>, where <code>x :: a</code> and <code>y :: b</code>.
We can cover the ⊥ case as we did with <code>Bool</code>, by ⊔-ing in <code>const (h ⊥)</code>.
For the <code>(x,y)</code> case, we can proceed just as in strict memoization, by uncurrying, memoizing the outer and inner functions (of <code>a</code> and of <code>b</code> respectively), and recurrying.
For details, see <em><a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">Elegant memoization with functional memo tries</a></em>.</p>

<p>Similarly for sum types.
(A value of type <code>Either a b</code> is either ⊥, or <code>Left x</code> or <code>Right y</code>, where <code>x :: a</code> and <code>y :: b</code>.)
And by following the treatment of products and sums, we can correctly memoize functions over any algebraic type.</p>

<h3>Related work</h3>

<h4>Lazy Memo-functions</h4>

<p>In 1985, John Hughes published a paper <em><a href="http://www.cse.chalmers.se/~rjmh/Papers/hughes_85_lazy.pdf" title="Paper by John Hughes">Lazy Memo-functions</a></em>, in which he points out the laziness-harming property of standard memoization.</p>

<blockquote>
  <p>[&#8230;] In a language with lazy evaluation this problem is aggravated: since verifying that two data-structures are equal requires that each be completely evaluated, all memoised functions are completely strict. This means they cannot be applied to circular or infinite arguments, or to arguments which (for one reason or another) cannot yet be completely evaluated. Therefore memo-functions cannot be combined with the most powerful features of lazy languages.</p>
</blockquote>

<p>John gives a laziness-friendlier alternative, which is to use the <em>addresses</em> rather than contents in the case of structured arguments.
Since it does force evaluation on atomic arguments, I don&#8217;t think it preserves non-strictness.
Moreover, it leads to redundant computation when structured arguments are equal but not pointer-equal.</p>

<h3>Conclusion</h3>

<p>Formulations of function memoization can be quite elegant and practical in a non-strict/lazy functional language.
In such a setting, however, I cannot help but want to correctly handle <em>all</em> functions, including non-strict ones.
This post gives a technique for doing so, making crucial use of the least upper bound (⊔) operator described in <a href="http://conal.net/blog/tag/unamb/" title="Posts on unambiguous choice and least upper bound">various other posts</a>.</p>

<p>Despite the many words above, the modification to strict memoization is simple: for a function <code>h</code>, given an argument <code>x</code>, in addition to indexing a memo trie with <code>x</code>, also evaluate <code>h ⊥</code>, and merge the information obtained from these two attempts (conceptually run in parallel).
Indexing a memo trie forces evaluation of <code>x</code>, which is a problem when <code>h</code> is non-strict and <code>x</code> evaluates to ⊥.
In exactly that case, however, <code>h ⊥</code> is not ⊥, and so provides exactly the information we need.
Moreover, information-monotonicity of <code>h</code> (a property of all computable functions) guarantees that <code>h ⊥ ⊑ h x</code>, so the information being merged is compatible.</p>

<p>Note that this condition is even stronger than compatibility, so perhaps we could use a more restricted and more efficient alternative to the fully general least upper bound.
The technique in <em><a href="http://conal.net/blog/posts/exact-numeric-integration/" title="blog post">Exact numeric integration</a></em> also used this restricted form.</p>

<p>How does this method for correct, non-strict memoization work in practice?
I guess the answer mainly depends on the efficiency and robustness of ⊔ (or of the restricted form mentioned just above).
The current implementation could probably be improved considerably if brought into the runtime system (RTS) and implemented by an RTS expert (which I&#8217;m not).</p>

<p>Information ordering and ⊔ play a central role in the denotational semantics of programming languages.
Since first stumbling onto a use for <code>⊔</code> (initially in its flat form, <code>unamb</code>), I&#8217;ve become very curious about how this operator might impact programming <em>practice</em> as well as theory.
My impression so far is that it is a powerful modularization tool, just as laziness is (as illustrated by John Hughes in <em><a href="http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html" title="Paper by John Hughes">Why Functional Programming Matters</a></em>).
I&#8217;m looking for more examples, to further explore this impression.</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=110&amp;md5=674554b8e359b757ff8d3d24cc6937e1"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/nonstrict-memoization/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fnonstrict-memoization&amp;language=en_GB&amp;category=text&amp;title=Non-strict+memoization&amp;description=I%26%238217%3Bve+written+a+few+posts+about+functional+memoization.+In+one+of+them%2C+Luke+Palmer+commented+that+the+memoization+methods+are+correct+only+for+strict+functions%2C+which+I+had+not+noticed...&amp;tags=memoization%2Ctrie%2Cunamb%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Denotational design with type class morphisms</title>
		<link>http://conal.net/blog/posts/denotational-design-with-type-class-morphisms</link>
		<comments>http://conal.net/blog/posts/denotational-design-with-type-class-morphisms#comments</comments>
		<pubDate>Thu, 19 Feb 2009 02:34:08 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[applicative functor]]></category>
		<category><![CDATA[arrow]]></category>
		<category><![CDATA[associated type]]></category>
		<category><![CDATA[functor]]></category>
		<category><![CDATA[monad]]></category>
		<category><![CDATA[monoid]]></category>
		<category><![CDATA[paper]]></category>
		<category><![CDATA[semantics]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[type class morphism]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=84</guid>
		<description><![CDATA[I&#8217;ve just finished a draft of a paper called Denotational design with type class morphisms, for submission to ICFP 2009. The paper is on a theme I&#8217;ve explored in several posts, which is semantics-based design, guided by type class morphisms. I&#8217;d love to get some readings and feedback. Pointers to related work would be particularly [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Denotational design with type class morphisms

Tags: paper, semantics, type class morphism, monoid, functor, applicative functor, monad, arrow, associated type, trie

URL: http://conal.net/blog/posts/denotational-design-with-type-class-morphisms/

-->

<!-- references -->

<!-- teaser -->

<p>I&#8217;ve just finished a draft of a paper called <em><a href="http://conal.net/papers/type-class-morphisms" title="paper">Denotational design with type class morphisms</a></em>, for submission to <a href="http://www.cs.nott.ac.uk/~gmh/icfp09.html" title="conference page">ICFP 2009</a>.
The paper is on a theme I&#8217;ve explored in <a href="http://conal.net/blog/tag/type-class-morphism/">several posts</a>, which is semantics-based design, guided by type class morphisms.</p>

<p>I&#8217;d love to get some readings and feedback.
Pointers to related work would be particularly appreciated, as well as what&#8217;s unclear and what could be cut.
It&#8217;s an entire page over the limit, so I&#8217;ll have to do some trimming before submitting.</p>

<p>The abstract:</p>

<blockquote>
  <p>Type classes provide a mechanism for varied implementations of standard
  interfaces. Many of these interfaces are founded in mathematical
  tradition and so have regularity not only of <em>types</em> but also of
  <em>properties</em> (laws) that must hold. Types and properties give strong
  guidance to the library implementor, while leaving freedom as well. Some
  of the remaining freedom is in <em>how</em> the implementation works, and some
  is in <em>what</em> it accomplishes.</p>
  
  <p>To give additional guidance to the <em>what</em>, without impinging on the
  <em>how</em>, this paper proposes a principle of <em>type class morphisms</em> (TCMs),
  which further refines the compositional style of denotational
  semantics. The TCM idea is simply that <em>the instance&#8217;s meaning is the
  meaning&#8217;s instance</em>. This principle determines the meaning of each type
  class instance, and hence defines correctness of implementation. In some
  cases, it also provides a systematic guide to implementation, and in
  some cases, valuable design feedback.</p>
  
  <p>The paper is illustrated with several examples of type, meanings, and
  morphisms.</p>
</blockquote>

<p>You can <a href="http://conal.net/papers/type-class-morphisms" title="paper">get the paper and see current errata here</a>.</p>

<p>The submission deadline is March 2, so comments before then are most helpful to me.</p>

<p>Enjoy, and thanks!</p>

<!--
**Edits**:

* 2009-02-09: just fiddling around
-->
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=84&amp;md5=8ce3b83d01ccfad97ade1469b72d2a04"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/denotational-design-with-type-class-morphisms/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fdenotational-design-with-type-class-morphisms&amp;language=en_GB&amp;category=text&amp;title=Denotational+design+with+type+class+morphisms&amp;description=I%26%238217%3Bve+just+finished+a+draft+of+a+paper+called+Denotational+design+with+type+class+morphisms%2C+for+submission+to+ICFP+2009.+The+paper+is+on+a+theme+I%26%238217%3Bve+explored+in+several...&amp;tags=applicative+functor%2Carrow%2Cassociated+type%2Cfunctor%2Cmonad%2Cmonoid%2Cpaper%2Csemantics%2Ctrie%2Ctype+class+morphism%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Simpler, more efficient, functional linear maps</title>
		<link>http://conal.net/blog/posts/simpler-more-efficient-functional-linear-maps</link>
		<comments>http://conal.net/blog/posts/simpler-more-efficient-functional-linear-maps#comments</comments>
		<pubDate>Mon, 20 Oct 2008 01:26:05 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[linear map]]></category>
		<category><![CDATA[math]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[type family]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=57</guid>
		<description><![CDATA[A previous post described a data type of functional linear maps. As Andy Gill pointed out, we had a heck of a time trying to get good performance. This note describes a new representation that is very simple and much more efficient. It&#8217;s terribly obvious in retrospect but took me a good while to stumble [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- teaser -->

<p
>A <a href="http://conal.net/blog/posts/functional-linear-maps/" title="Blog post: &quot;Functional linear maps&quot;"
  >previous post</a
  > described a data type of functional linear maps. As <a href="http://www.unsafeperformio.com/index.php" title="Andy Gill's home page"
  >Andy Gill</a
  > <a href="http://blog.unsafeperformio.com/?p=23" title="Blog post: &quot;Performance problems with functional representation of derivatives&quot;"
  >pointed out</a
  >, we had a heck of a time trying to get good performance. This note describes a new representation that is <em
  >very simple</em
  > and much more efficient. It&#8217;s terribly obvious in retrospect but took me a good while to stumble onto.</p
>

<p
>The Haskell module described here is part of the <a href="http://haskell.org/haskellwiki/vector-space" title="Library wiki page: &quot;vector-space&quot;"
  >vector-space library</a
  > (version 0.5 or later) and requires ghc version 6.10 or better (for associated types).</p
>

<p
><strong
  >Edits</strong
  >:</p
>

<ul
><li
  >2008-11-09: Changed remarks about versions. The vector-space version 0.5 depends on ghc 6.10.</li
  ><li
  >2008-10-21: Fixed the <a href="http://haskell.org/haskellwiki/vector-space" title="Library wiki page: &quot;vector-space&quot;"
    >vector-space library</a
    > link in the teaser.</li
  ></ul
>

<p><span id="more-57"></span></p>

<div id="linear-maps"
><h3
  >Linear maps</h3
  ><p
  >Semantically, a <em
    >linear map</em
    > is a function <code
    >f &#8759; a &#8594; b</code
    > such that, for all scalar values <code
    >s</code
    > and &quot;vectors&quot; <code
    >u, v &#8759; a</code
    >, the following properties hold:</p
  ><div class=math-inset>
<p
  ><span class="math"
    ><em
      >f</em
      > (<em
      >s</em
      > ⋅ <em
      >u</em
      >) = <em
      >s</em
      > ⋅ <em
      >f</em
      > <em
      >u</em
      ></span
    ></p
  ><p
  ><span class="math"
    ><em
      >f</em
      > (<em
      >u</em
      > + <em
      >v</em
      >) = <em
      >f</em
      > <em
      >u</em
      > + <em
      >f</em
      > <em
      >v</em
      ></span
    ></p
  ></div>
<p
  >By repeated application of these properties,</p
  ><div class=math-inset>
<p
  ><span class="math"
    ><em
      >f</em
      > (<em
      >s</em
      ><sub
      >1</sub
      > ⋅ <em
      >u</em
      ><sub
      >1</sub
      > + ⋯ + <em
      >s</em
      ><sub
      ><em
    >n</em
    ></sub
      > ⋅ <em
      >u</em
      ><sub
      ><em
    >n</em
    ></sub
      >) = <em
      >s</em
      ><sub
      >1</sub
      > ⋅ <em
      >f</em
      > <em
      >u</em
      ><sub
      >1</sub
      > + ⋯ + <em
      >s</em
      ><sub
      ><em
    >n</em
    ></sub
      > ⋅ <em
      >f</em
      > <em
      >u</em
      ><sub
      ><em
    >n</em
    ></sub
      ></span
    ></p
  ></div>
<p
  >Taking the <em
    >u<sub>i</sub></em
    > as basis vectors, this form implies that a linear function is determined by its behavior on any <a href="http://en.wikipedia.org/wiki/Basis_(linear_algebra)" title="Wikipedia article"
    >basis</a
    > of its domain type.</p
  ><p
  >Therefore, a linear function can be represented simply as a function from a basis, using the representation described in <a href="http://conal.net/blog/posts/vector-space-bases-via-type-families/" title="Blog post: &quot;Vector space bases via type families&quot;"
    ><em
      >Vector space bases via type families</em
      ></a
    >.</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > u <span class="fu"
      >:-*</span
      > v <span class="fu"
      >=</span
      > <span class="dt"
      >Basis</span
      > u &#8594; v<br
       /></code
    ></pre
  ><p
  >The semantic function converts from <code
    >(u :-* v)</code
    > to <code
    >(u &#8594; v)</code
    >. It decomposes a source vector into its coordinates, applies the basis function to basis representations, and linearly combines the results.</p
  ><pre class="sourceCode haskell"
  ><code
    >lapply <span class="dv"
      >&#8759;</span
      > ( <span class="dt"
      >VectorSpace</span
      > u, <span class="dt"
      >VectorSpace</span
      > v<br
       />         , <span class="dt"
      >Scalar</span
      > u <span class="fu"
      >~</span
      > <span class="dt"
      >Scalar</span
      > v, <span class="dt"
      >HasBasis</span
      > u ) &#8658;<br
       />         (u <span class="fu"
      >:-*</span
      > v) &#8594; (u &#8594; v)<br
       />lapply lm <span class="fu"
      >=</span
      > &#955; u &#8594; sumV [s <span class="fu"
      >*^</span
      > lm b <span class="fu"
      >|</span
      > (b,s) &#8592; decompose u]<br
       /></code
    ></pre
  ><p
  >or</p
  ><pre class="sourceCode haskell"
  ><code
    >lapply lm <span class="fu"
      >=</span
      > linearCombo &#8728; <span class="fu"
      >fmap</span
      > (first lm) &#8728; decompose<br
       /></code
    ></pre
  ><p
  >The reverse function is easier. Convert a function <code
    >f</code
    >, presumed linear, to a linear map representation:</p
  ><pre class="sourceCode haskell"
  ><code
    >linear <span class="dv"
      >&#8759;</span
      > (<span class="dt"
      >VectorSpace</span
      > u, <span class="dt"
      >VectorSpace</span
      > v, <span class="dt"
      >HasBasis</span
      > u) &#8658;<br
       />         (u &#8594; v) &#8594; (u <span class="fu"
      >:-*</span
      > v)<br
       /></code
    ></pre
  ><p
  >It suffices to apply <code
    >f</code
    > to basis values:</p
  ><pre class="sourceCode haskell"
  ><code
    >linear f <span class="fu"
      >=</span
      > f &#8728; basisValue<br
       /></code
    ></pre
  ></div
>

<div id="memoization"
><h3
  >Memoization</h3
  ><p
  >The idea of the linear map representation is to reconstruct an entire (linear) function out of just a few samples. In other words, we can make a very small sampling of function's domain, and re-use those values in order to compute the function's value at <em
    >all</em
    > domain values. As implemented above, however, this trick makes function application more expensive, not less. If <code
    >lm = linear f</code
    >, then each use of <code
    >lapply lm</code
    > can apply <code
    >f</code
    > to the value of every basis element, and then linearly combine results.</p
  ><p
  >A simple trick fixes this efficiency problem: <em
    >memoize</em
    > the linear map. We could do the memoization privately, e.g.,</p
  ><pre class="sourceCode haskell"
  ><code
    >linear f <span class="fu"
      >=</span
      > memo (f &#8728; basisValue)<br
       /></code
    ></pre
  ><p
  >If <code
    >lm = linear f</code
    >, then no matter how many times <code
    >lapply lm</code
    > is applied, the function <code
    >f</code
    > can only get applied as many times as the dimension of the domain of <code
    >f</code
    >.</p
  ><p
  >However, there are several other ways to make linear maps, and it would be easy to forget to memoize each combining form. So, instead of the function representation above, I ensure that the function be memoized by representing it as a <a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="Blog post: &quot;Elegant memoization with functional memo tries&quot;"
    >memo trie</a
    >.</p
  ><pre class="sourceCode haskell"
  ><code
    ><span class="kw"
      >type</span
      > u <span class="fu"
      >:-*</span
      > v <span class="fu"
      >=</span
      > <span class="dt"
      >Basis</span
      > u &#8603; v<br
       /></code
    ></pre
  ><p
  >The conversion functions <code
    >linear</code
    > and <code
    >lapply</code
    > need just a little tweaking. Split <code
    >memo</code
    > into its definition <code
    >untrie &#8728; trie</code
    >, and then move the second phase (<code
    >untrie</code
    >) into <code
    >lapply</code
    >. We'll also have to add <code
    >HasTrie</code
    > constraints:</p
  ><pre class="sourceCode haskell"
  ><code
    >linear <span class="dv"
      >&#8759;</span
      > ( <span class="dt"
      >VectorSpace</span
      > u, <span class="dt"
      >VectorSpace</span
      > v<br
       />         , <span class="dt"
      >HasBasis</span
      > u, <span class="dt"
      >HasTrie</span
      > (<span class="dt"
      >Basis</span
      > u) ) &#8658;<br
       />         (u &#8594; v) &#8594; (u <span class="fu"
      >:-*</span
      > v)<br
       />linear f <span class="fu"
      >=</span
      > trie (f &#8728; basisValue)<br
       /><br
       />lapply <span class="dv"
      >&#8759;</span
      > ( <span class="dt"
      >VectorSpace</span
      > u, <span class="dt"
      >VectorSpace</span
      > v, <span class="dt"
      >Scalar</span
      > u <span class="fu"
      >~</span
      > <span class="dt"
      >Scalar</span
      > v<br
       />         , <span class="dt"
      >HasBasis</span
      > u, <span class="dt"
      >HasTrie</span
      > (<span class="dt"
      >Basis</span
      > u) ) &#8658;<br
       />         (u <span class="fu"
      >:-*</span
      > v) &#8594; (u &#8594; v)<br
       />lapply lm <span class="fu"
      >=</span
      > linearCombo &#8728; <span class="fu"
      >fmap</span
      > (first (untrie lm)) &#8728; decompose<br
       /></code
    ></pre
  ><p
  >Now we can build up linear maps conveniently and efficiently by using the operations on memo tries shown in <a href="http://conal.net/blog/posts/composing-memo-tries/" title="blog post"
    ><em
      >Composing memo tries</em
      ></a
    >. For instance, suppose that <code
    >h</code
    > is a linear function of two arguments (linear in <em
    >both</em
    >, not it <em
    >each</em
    >) and <code
    >m</code
    > and <code
    >n</code
    > are two linear maps. Then <code
    >liftA2 h m n</code
    > is the linear function that applies <code
    >h</code
    > to the results of <code
    >m</code
    > and <code
    >n</code
    >.</p
  ><pre class="sourceCode haskell"
  ><code
    >lapply (liftA2 h m n) a <span class="fu"
      >=</span
      > h (lapply m a) (lapply n a)<br
       /></code
    ></pre
  ><p
  >Exploiting the applicative functor instance for functions, we get another formulation:</p
  ><pre class="sourceCode haskell"
  ><code
    >lapply (liftA2 h m n) <span class="fu"
      >=</span
      > liftA2 h (lapply m) (lapply n)<br
       /></code
    ></pre
  ><p
  >In other words, the meaning of a <code
    >liftA2</code
    > is the <code
    >liftA2</code
    > of the meanings, as discussed in <a href="http://conal.net/blog/posts/simplifying-semantics-with-type-class-morphisms" title="blog post"
    ><em
      >Simplifying semantics with type class morphisms</em
      ></a
    >.</p
  ></div
>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=57&amp;md5=a424d64bbbfc4572f7de631b69af7b6f"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/simpler-more-efficient-functional-linear-maps/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fsimpler-more-efficient-functional-linear-maps&amp;language=en_GB&amp;category=text&amp;title=Simpler%2C+more+efficient%2C+functional+linear+maps&amp;description=A+previous+post+described+a+data+type+of+functional+linear+maps.+As+Andy+Gill+pointed+out%2C+we+had+a+heck+of+a+time+trying+to+get+good+performance.+This+note...&amp;tags=linear+map%2Cmath%2Cmemoization%2Ctrie%2Ctype+family%2Cblog" type="text/html" />
	</item>
	</channel>
</rss>
