<?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; memoization</title>
	<atom:link href="http://conal.net/blog/tag/memoization/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>Memoizing polymorphic functions via unmemoization</title>
		<link>http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization</link>
		<comments>http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization#comments</comments>
		<pubDate>Mon, 27 Sep 2010 22:38:46 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[memoization]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=203</guid>
		<description><![CDATA[Last year I wrote two posts on memoizing polymorphic functions. The first post gave a solution of sorts that uses a combination of patricia trees (for integer-keyed maps), stable names, and type equality proofs. The second post showed how to transform some functions that do not quite fit the required form so that they do [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Memoizing polymorphic functions via unmemoization

Tags: memoization

URL: http://conal.net/blog/posts/memoizing-polymorphic-functions-via-unmemoization/

-->

<!-- references -->

<!-- teaser -->

<p>Last year I wrote two posts on memoizing polymorphic functions.
The <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-part-one/" title="blog post">first post</a> gave a solution of sorts that uses a combination of patricia trees (for integer-keyed maps), stable names, and type equality proofs.
The <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two/" title="blog post">second post</a> showed how to transform some functions that do not quite fit the required form so that they do fit.</p>

<p>Dan Piponi wrote a follow-up post <em><a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Memoizing Polymorphic Functions with High School Algebra and Quantifiers</a></em> showing a different approach that was more in the spirit of type-directed functional memoization, as it follows purely from mathematical properties, free of the deeply operational magic of stable names.
Recently, I finally studied and worked with Dan&#8217;s post enough to understand what he did.
It&#8217;s <em>very</em> clever and beautiful!</p>

<p>This post re-presents Dan&#8217;s elegant insight as I understand it, via some examples that helped it come together for me.</p>

<!--
**Edits**:

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

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

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

<h3>Playing with quantifiers</h3>

<p>Consider the type of <code>fmap</code>:</p>

<pre><code>fmap :: Functor f ⇒ (a → b) → f a → f b
</code></pre>

<p>and make the type quantification explicit</p>

<pre><code>fmap :: ∀ f a b. Functor f ⇒ (a → b) → f a → f b
</code></pre>

<p>Since the <code>Functor</code> constraint depends only on <code>f</code>, restrict the scope of the other quantifiers:</p>

<pre><code>fmap :: ∀ f. Functor f ⇒ ∀ a b. (a → b) → f a → f b
</code></pre>

<p>We cannot further restrict the scope of <code>a</code> or <code>b</code>, because the first argument of the function involves both <code>a</code> and <code>b</code>.
The <em>second</em> argument, however, ignores <code>b</code>, so let&#8217;s next <code>flip</code> the arguments.</p>

<pre><code>flip fmap :: ∀ f. Functor f ⇒ ∀ a b. f a → (a → b) → f b
</code></pre>

<p>and restrict the scope of <code>b</code>:</p>

<pre><code>flip fmap :: ∀ f. Functor f ⇒ ∀ a. f a → ∀ b. (a → b) → f b
</code></pre>

<p>Next introduce names:</p>

<pre><code>type Yo f a = ∀ b. (a → b) → f b

toYo :: Functor f ⇒ ∀ a. f a → Yo f a
toYo = flip fmap
</code></pre>

<p>So <code>flip fmap</code> is a way to map <code>f a</code> to <code>Yo f a</code>.
The reverse is easier:</p>

<pre><code>fromYo :: ∀ a. Yo f a → f a
fromYo f = f id
</code></pre>

<p>GHC&#8217;s type-checker won&#8217;t really let this <code>toYo</code> definition fly, because of persnickety details about universal quantification.
Instead, inline <code>flip</code> and simplify:</p>

<pre><code>toYo x p = fmap p x
</code></pre>

<h3>The Yoneda lemma</h3>

<p>Are the functions <code>fromYo</code> and <code>toYo</code> inverses of each other?
The <a href="http://en.wikipedia.org/wiki/Yoneda_lemma">Yoneda lemma</a> from category theory says yes.
The following proof is taken from <a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Dan&#8217;s post</a>:</p>

<pre><code>  fromYo (toYo x)
≡ fromYo (λ p → fmap p x)
≡ (λ p → fmap p x) id
≡ fmap id x
≡ x                    -- a functor law

  toYo (fromYo f)
≡ toYo (f id)
≡ λ p → fmap p (f id)
≡ λ p → f (fmap p id)  -- f is polymorphic/natural
≡ λ p → f (p ∘ id)     -- fmap on functions is (∘)
≡ λ p → f p
≡ f
</code></pre>

<p>Note the critical role of <em>naturality</em>, which holds of parametrically polymorphic functions in Haskell.
To say that a function <code>f</code> is <em>natural</em> means that it commutes with <code>fmap p</code>, for all functions <code>p</code>, i.e.,</p>

<pre><code>fmap p ∘ f  ≡  f ∘ fmap p
</code></pre>

<h2>Playing with the Yoneda lemma</h2>

<p>With a little type massaging, the Yoneda lemma can be applied more broadly than its literal form.
For instance, consider the type <code>(∀ b. f b)</code> for a functor <code>f.</code>
Does it have a simpler isomorphic form?</p>

<p>The Yoneda lemma applies to types of the form <code>∀ b. (a → b) → f b</code> for some type <code>a</code>.
We don&#8217;t have a function to <code>f b</code>, but we could fake one by introducing a unit argument:</p>

<pre><code>  ∀ b. f b
≅ ∀ b. 1 → f b
</code></pre>

<p>Warning: this step is only correct for strict functions, so I&#8217;m fudging here.</p>

<p>I&#8217;m using &#8220;<code>1</code>&#8221; for the Haskell type <code>()</code>.
I&#8217;ll also use &#8220;×&#8221; and &#8220;+&#8221; for product and sum types in these examples, in place of the Haskell names &#8220;<code>(,)</code>&#8221; and &#8220;<code>Either</code>&#8220;.
For parsing the examples, × binds more tightly than than +, which binds more tightly than →.</p>

<p>We haven&#8217;t gotten to the required form yet, because we have <code>1</code> where we need <code>a → b</code> for some <code>a</code>.
Hm.
For what type <code>a</code> is there only one function from <code>a</code> to <code>b</code>, where <code>b</code> is <em>arbitrary</em> type?
By choosing <code>b</code> to have at least two elements, we can make different functions of type <code>a → b</code> by mapping some element of <code>a</code> to different elements of <code>b</code>.
Unless <code>a</code> has no elements at all!</p>

<p>Let <code>0</code> be the type with no elements:</p>

<pre><code>data 0
</code></pre>

<p>This type is usually named &#8220;<code>Void</code>&#8221; in Haskell.</p>

<p>Since there are no values of type <code>0</code>, there is exactly one function from <code>0</code> to any other type <code>b</code>, i.e., <code>1 ≅ 0 → b</code>.
(This type isomorphism corresponds to a familiar equality on numbers.)
Thus continuing our definition from above</p>

<pre><code>⋯ ≅ ∀ b. (0 → b) → f b
</code></pre>

<p>Now we have the form covered directly by the Yoneda lemma</p>

<pre><code>⋯ ≅ f 0
</code></pre>

<p>Oh dear, I keep lying to you.
The step from <code>1</code> to <code>0 → b</code> is also valid only for strict functions, or for a <code>0</code> without ⊥.</p>

<p>Replaying this derivation,</p>

<pre><code>  ∀ b. f b
≅ ∀ b. 1 → f b
≅ ∀ b. (0 → b) → f b
≅ f 0
</code></pre>

<p>Next try a simpler example: <code>∀ a. a</code>.
This time, temporarily introduce the identity functor <code>Id</code>:</p>

<pre><code>  ∀ a. a
≅ ∀ a. Id a
≅ Id 0
≅ 0
</code></pre>

<p>which is correct, as there is no non-⊥ value of type <code>∀ a. a</code>.</p>

<p>Next try <code>∀ a. a → a</code>:</p>

<pre><code>  ∀ a. a → a
≅ ∀ a. (1 → a) → Id a
≅ Id 1
≅ 1
</code></pre>

<p>And indeed, there is only one (non-⊥) function of type <code>∀ a. a → a</code>.</p>

<p>Here&#8217;s a simple specialization of the Yoneda lemma (also mentioned in <a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Dan&#8217;s post</a>):</p>

<pre><code>  ∀ b. (a → b) → b
≅ ∀ b. (a → b) → Id b
≅ Id a
≅ a
</code></pre>

<p>These examples prove the existence of bijection, but we can also synthesize the bijections, as <a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Dan Piponi demonstrates</a>.
The bijections then let us memoize.</p>

<h3>Memoizing polymorphic functions via <em>unmemoization</em></h3>

<p>Let&#8217;s look at the Yoneda lemma again.
Recall</p>

<pre><code>type Yo f a = ∀ b. (a → b) → f b
</code></pre>

<p>For all natural transformations <code>f</code> (in Haskell polymorphic functions  <a href="http://blog.sigfpe.com/2006/11/yoneda-lemma.html" title="blog post by Dan Piponi">&#8220;with no funny stuff&#8221;</a>), the Yoneda lemma says that</p>

<pre><code>Yo f a ≅ f a
</code></pre>

<p>where the conversions between <code>Yo f a</code> and <code>f a</code> are very simple: <code>($ id)</code> and <code>flip fmap</code>.</p>

<p>Now suppose we want to memoize a polymorphic function <code>∀ a. T a → f a</code>.
The Yoneda lemma suggests that we find some <code>Q</code> such that <code>T a ≅ Q → a</code>, in which case</p>

<pre><code>  ∀ a. T a → f a
≅ ∀ a. (Q → a) → f a
≅ f Q
</code></pre>

<p>How do <code>Q</code> and <code>T</code> relate?
<code>T</code> is a memo table for <code>Q</code>.
In other words, <code>Q</code> is the type of <em>indices</em> into <code>T</code>.
In still other words, <code>Q</code> is an <em>unmemoization</em> of <code>T</code>.</p>

<p>This trick is from Dan Piponi&#8217;s post <em><a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Memoizing Polymorphic Functions with High School Algebra and Quantifiers</a></em>.
We started with a polymorphic function, and we ended up with a data structure, i.e., we have <em>memoized</em>.
But the critical step one the route to memoization was <em>unmemoization</em> (of <code>T</code>).
Delightful!</p>

<p>We saw some very simple examples in the previous section.
Let&#8217;s next look at some more examples.</p>

<h4>Pairs</h4>

<p>How can we memoize polymorphic functions on pairs, i.e., functions of type <code>∀ a. a × a → f a</code>?</p>

<p>We can work out an answer by appealing to the same laws of exponents used in <a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post">memoization</a>, but now applied in reverse (to create function types instead of eliminate them).</p>

<pre><code>  a × a
≅ (1 → a) × (1 → a)
≅ (1 + 1) → a
≅ Bool → a
</code></pre>

<p>Again, I&#8217;m handling only <em>strict</em> functions here.</p>

<p>Therefore,</p>

<pre><code>(∀ a. a × a → f a)  ≅  f Bool
</code></pre>

<p>Spelling out this conclusion (once more for practice),</p>

<pre><code>  ∀ a. a × a      → f a
≅ ∀ a. (Bool → a) → f a
≅ f Bool
</code></pre>

<p>A variation on this derivation is to make the pairing functor explicit as</p>

<pre><code>data P a = P a a
</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><code>type P = Id :*: Id
</code></pre>

<p>Now imagine we have a type family <code>UnTrie</code> that acts as an inverse to <code>Trie</code></p>

<pre><code>type UnTrie P = UnTrie (Id :*: Id)
              = UnTrie Id + UnTrie Id
              = 1 + 1
              ≅ Bool
</code></pre>

<h4>Streams</h4>

<p>A type of streams (infinite-only lazy lists) is like the type of lists but without a nil case:</p>

<pre><code>data Stream a = Cons a (Stream a)
</code></pre>

<p>How can we memoize <code>∀ a. Stream a → f a</code>?
We simply ask: for what type is <code>Stream</code> a memo table?
Or: what type naturally indexes a stream?
An answer is natural numbers in Peano form:</p>

<pre><code>data Nat = Zero | Succ Nat
</code></pre>

<p>Memoizing <code>Nat → a</code> 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> leads to <code>Stream a</code>.</p>

<p>Therefore,</p>

<pre><code>∀ a. Stream a → f a  ≅  f Nat
</code></pre>

<p>In case we didn&#8217;t think of <code>Nat</code> right off, we can derive it systematically.
We&#8217;ll appeal to the same three laws of exponents used in memoization, but now applied in reverse (to create function types instead of eliminate them).</p>

<p>Look for a type <code>N</code> such that <code>N → a ≅ Stream a</code>.</p>

<pre><code>  N → a
≅ Stream a
≅ a × Stream a
≅ (1 → a) × (N → a)   --  coinductively
≅ (1 + N) → a
</code></pre>

<p>A sufficient condition then is that <code>N ≅ 1 + N</code>, which condition then translates to a recursive data type definition:</p>

<pre><code>data N = Z | S N
</code></pre>

<p>or</p>

<pre><code>data Nat = Zero | Succ Nat
</code></pre>

<h4>Infinite binary trees</h4>

<p>Next let&#8217;s look at one form of binary trees, having values at each node and only infinite paths.</p>

<pre><code>data BTree a = BTree a (BTree a) (BTree a)
</code></pre>

<p>and let <code>B</code> be an unmemoized form of <code>BTree</code>, or type of indices of <code>BTree</code>, i.e., <code>B → a ≅ BTree a</code>.</p>

<pre><code>  B → a
≅ BTree a
≅ a × BTree a × BTree a
≅ (1 → a) × (B → a) × (B → a)
≅ (1 + B + B) → a
</code></pre>

<p>A sufficient definition for <code>B</code> is then <code>B = 1 + B + B</code>.
Or in legal Haskell form (no recursive type synonyms):</p>

<pre><code>data B = Z | E B | O B
</code></pre>

<p>We can think of this data type as representing natural numbers in <a href="http://en.wikipedia.org/wiki/Endianness">little endian</a> binary.
Expanding the type and constructor names:</p>

<pre><code>data BinNat = Zero | Even BinNat | Odd BinNat
</code></pre>

<h4>A variation on infinite binary trees</h4>

<p>We might have written our tree type differently:</p>

<pre><code>data BTree a = BTree a (P (BTree a))
</code></pre>

<p>where <code>P</code> is the pairing functor.
Again, let <code>B</code> be the sought index type for (unmemoization of) <code>BTree</code></p>

<pre><code>  B → a
≅ BTree a
≅ a × P (BTree a)
≅ (1 → a) × (Bool → BTree a)
≅ (1 → a) × (Bool → B → a)     ­- coinductively
≅ (1 → a) × (Bool × B → a)
≅ 1 + Bool × B → a
</code></pre>

<p>which then leads to a slightly different representation for our index type:</p>

<pre><code>data BinNat = Zero | NonZero Bool BinNat
</code></pre>

<p>We&#8217;ve made the <em>bit</em> type explicit.
In this form, <code>BinNat</code> is a little-endian list of bits, so we can redefine it as such:</p>

<pre><code>type BinNat = [Bool]
</code></pre>

<p>and the desired isomorphism</p>

<pre><code>(∀ a. BTree a → f a)  ≅  f [Bool]
</code></pre>

<h4>Generalized infinite trees</h4>

<p>The functor <code>P</code> and the type <code>Bool</code> have an important relationship to each other in the previous derivation, namely <code>Bool</code> is the index type for <code>P</code>.
We can play this same trick for <em>all</em> index types and their corresponding trie (memoization) functors.
Generalize from binary trees:</p>

<pre><code>data Tree d a = Tree a (d :→: Tree d a)
</code></pre>

<p>where &#8220;<code>k :→: v</code>&#8221; is the type of memo tries over domain type <code>k</code>, with range <code>v</code>.
(The trie structure is driven entirely by <code>k</code>.)
See other <a href="http://conal.net/blog/tag/memoization/" title="Posts on memoization">posts on memoization</a>.</p>

<p>These generalized trees are indexed by little-endian natural numbers over a &#8220;digit&#8221; type <code>d</code>.
Generalizing the proof for binary trees we get</p>

<pre><code>Tree d a ≅ [d] → a
</code></pre>

<p>Details:</p>

<pre><code>  Tree d a
≅ a × (d :→: Tree d a)
≅ a × (d → Tree d a)
≅ a × (d → [d] → a)   ­- coinductively
≅ a × (d × [d] → a)
≅ (1 → a) × (d × [d] → a)
≅ (1 + d × [d] → a)
≅ [d] → a
</code></pre>

<p>So the Yoneda lemma tells us that</p>

<pre><code>(∀ a. Tree d a → f a) ≅ f [d]
</code></pre>

<p>Specializing, we get binary trees indexed by binary numbers, and streams (&#8220;unary trees&#8221;) indexed by unary numbers:</p>

<pre><code>type BTree = Tree Bool
type Stream = Tree 1
</code></pre>

<p>For rose trees we would need an index type <code>d</code> whose memo tries are lists, i.e., <code>d :→: v ≅ [v]</code> for all types <code>v</code>.
I don&#8217;t think there is such a type <code>d</code>, since <code>[v]</code> is isomorphic to a <code>sum</code> type, and memoization&#8211;like exponentiation­-doesn&#8217;t produce sums.
I&#8217;ll return to this question below.</p>

<h4><code>Maybe</code></h4>

<p>Let&#8217;s try something a bit simpler: memoizing <code>∀ a. Maybe a → f a</code>.
Of what domain type is <code>Maybe a</code> a memo table?</p>

<p>Oops.
None of the memoizing transformations generates a sum type such as <code>Maybe</code>.
So we&#8217;ll have to try something else.
First split the domain sum, yielding a product, and then proceed with the summands.</p>

<pre><code>  ∀ a. Maybe a → f a
≅ ∀ a. (1 + a) → f a
≅ ∀ a. (1 → f a) × (a → f a)
≅ ∀ a. f a × (a → f a)
≅ (∀ a. f a) × (∀ a. a → f a)
≅ f 0 × f 1
</code></pre>

<h4>Lists</h4>

<p>(Even more than the previous sections, this one is a regurgitation of material from <a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Dan&#8217;s post</a>.)</p>

<p>We&#8217;ve covered <code>Maybe</code> and <code>Stream</code> above, so lists might work out with a combination of techniques.</p>

<pre><code>  ∀ a. [a] → f a
≅ ∀ a. (1 + a × [a]) → f a
≅ ∀ a. (1 → f a) × (a × [a] → f a)
≅ ∀ a. f a × (a × [a] → f a)
≅ (∀ a. f a) × (∀ a. a × [a] → f a)
≅ f 0 × (∀ a. a × [a] → f a)
</code></pre>

<p>How to continue from here?
The remaining polymorphic function doesn&#8217;t look quite like what we started with.
Dan&#8217;s trick was to generalize (as <a href="http://en.wikipedia.org/wiki/How_to_Solve_It">Pólya recommended</a>), replacing <code>[a]</code> with <code>a^n × [a]</code>, starting the process with <code>n ≡ 0</code>.
Dan generalized even more, where <code>n</code> is an arbitrary <em>type</em>, to <code>(n → a) × [a]</code>, starting with <code>n ≡ 0</code> (zero).</p>

<pre><code>type T f n = ∀ a. (n → a) × [a] → f a
</code></pre>

<p>Then</p>

<pre><code>  T f n
≡ ∀ a. (n → a) × [a] → f a
≅ ∀ a. (n → a) × (1 + a × [a]) → f a
≅ ∀ a. (n → a) × 1 + (n → a) × a × [a] → f a
≅ ∀ a. (n → a) + (n → a) × a × [a] → f a
≅ ∀ a. (n → a) + (n → a) × (1 → a) × [a] → f a
≅ ∀ a. (n → a) + ((n + 1 → a) × [a] → f a
≅ ∀ a. ((n → a) → f a) × ((n + 1 → a) × [a] → f a)
≅ (∀ a. (n → a) → f a) × (∀ a. (n + 1 → a) × [a] → f a)
≅ f n × (∀ a. (n + 1 → a) × [a] → f a)
≡ f n × T f (n + 1)
</code></pre>

<p>Inductively, this isomorphism gives rise to an function-free type:</p>

<pre><code>type Q f n = f n × Q f (n + 1)
</code></pre>

<p>for which</p>

<pre><code>Q f n ≅ T f n
</code></pre>

<p>Since Haskell doesn&#8217;t handle recursive type synonyms, instead define a new <code>data</code> type, as in <a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Dan&#8217;s post</a>:</p>

<pre><code>data Q f n = Q (f n) (Q f (n + 1))
</code></pre>

<p>Since <code>n + 1 ≅ Maybe n</code>, we could instead define</p>

<pre><code>data Q f n = Q (f n) (Q f (Maybe n))
</code></pre>

<p>As Dan described, the trick in this derivation is to rework the type <code>[a]</code> in polynomial form <code>[a] ≅ 1 + a + a^2 + a^3 + ⋯</code>.
Then memoization works out fairly easily:</p>

<pre><code>(∀ a. [a] → f a) ≅ f 0 × f 1 × f 2 × ...
</code></pre>

<p>Where the types <code>0</code>, <code>1</code>, <code>2</code>, … have 0, 1, 2, … elements, respectively.
We can choose <code>2 = Maybe 1</code>, <code>3 = Maybe 2</code>, … or <code>2 = 1 + 1</code>, <code>3 = 2 + 1</code>, ….</p>

<h3>Where to go from here?</h3>

<h4>Algebraic types</h4>

<p>I&#8217;m not entirely satisfied with this solution to memoizing polymorphic functions over lists (and other algebraic types).
I want to find another angle that doesn&#8217;t require this conversion to polynomial form.
Partly because I don&#8217;t think it handles infinite lists (as Dan mentioned).
I suppose one could just add the infinite case:</p>

<pre><code>(∀ a. [a] → f a) ≅ (f 0 × f 1 × f 2 × ...) × f Nat ≅ Q f n × f Nat
</code></pre>

<p>How to justify/derive this isomorphism?
And how to get non-strict memoization, including sharing of work for computations that use bounded input?</p>

<p>I also want a representation that&#8217;s friendly to non-strict memoization, sharing work on common prefixes.</p>

<h4>Constructing isomorphisms</h4>

<p>As always, type isomorphism is at the core of functional memoization.
However, it&#8217;s not enough to show that isomorphisms exist.
We have to <em>construct</em> the isomorphisms in order to implement a harder memoization problem via an easier one.</p>

<p><a href="http://blog.sigfpe.com/2009/11/memoizing-polymorphic-functions-with.html" title="blog post by Dan Piponi">Dan&#8217;s post</a> builds up some isomorphisms.
The process is rather tedious, and I&#8217;d like to make it much easier.
I&#8217;ve tinkered with making this process much more elegant by applying <em><a href="http://conal.net/blog/posts/semantic-editor-combinators/" title="blog post">Semantic editor combinators</a></em> in their generalized form (as in the paper <em><a href="http://conal.net/papers/Eros/" title="Paper">Tangible Functional Programming</a></em>), using bijections as the <a href="DeepArrow">deep arrow</a>.
So far, however, I&#8217;m struggling unsuccessfully against awkwardness in the handling of explicit polymorphism (higher-rank types).</p>

<h4>Automation</h4>

<p>In my other <a href="http://conal.net/blog/tag/memoization/" title="Posts on memoization">posts on memoization</a>, functions are memoized in a fully automatic, type-driven way.
Can the same be done somehow with polymorphic functions?</p>

<h4>Non-strictness</h4>

<p>I fudged treatment of non-strictness above.
Can non-strict polymorphic functions be handled correctly and elegantly, perhaps with a simple combination of previous techniques or perhaps with new ones?</p>

<h4>Other memoization tools?</h4>

<p>Dan&#8217;s use of the Yoneda lemma to memoize polymorphic functions (via <em>unmemoization</em>) came as a surprise to me.
Are there other deep properties of typed, pure functional programming that give rise to additional tools for memoization?
One place to start looking is additional free theorems.</p>

<h4>Relationship to numeric representations</h4>

<p>In <em><a href="http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504" title="book by Chris Okasaki">Purely Functional Data Structures</a></em>, Chris Okasaki points out a correspondence between number representation schemes and collection data structures.
How does this correspondence relate to memoization?</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=203&amp;md5=3c1df60132147b7dfd9e3c1eeead2a76"><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-polymorphic-functions-via-unmemoization/feed</wfw:commentRss>
		<slash:comments>12</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-polymorphic-functions-via-unmemoization&amp;language=en_GB&amp;category=text&amp;title=Memoizing+polymorphic+functions+via+unmemoization&amp;description=Last+year+I+wrote+two+posts+on+memoizing+polymorphic+functions.+The+first+post+gave+a+solution+of+sorts+that+uses+a+combination+of+patricia+trees+%28for+integer-keyed+maps%29%2C+stable+names%2C...&amp;tags=memoization%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>Memoizing polymorphic functions &#8211; part two</title>
		<link>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two</link>
		<comments>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two#comments</comments>
		<pubDate>Fri, 12 Jun 2009 22:04:01 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[polymorphism]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=89</guid>
		<description><![CDATA[Part one of this series introduced the problem of memoizing functions involving polymorphic recursion. The caching data structures used in memoization typically handle only one type of argument at a time. For instance, one can have finite maps of differing types, but each concrete finite map holds just one type of key and one type [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Memoizing polymorphic functions - part two
Tags: memoization, polymorphism

URL: http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two/

-->

<!-- references -->

<!-- teaser -->

<p><a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-part-one/" title="Blog post">Part one</a> of this series introduced the problem of memoizing functions involving polymorphic recursion.
The caching data structures used in memoization typically handle only one <em>type</em> of argument at a time.
For instance, one can have finite maps of differing types, but each concrete finite map holds just one type of key and one type of value.</p>

<p>I extended memoization to handle polymorphic recursion by using an existential type together with a reified type of types.
This extension works (afaik), but it is restricted to a particular form for the type of the polymorphic function being memoized, namely</p>

<pre><code>-- Polymorphic function
type k :--&gt; v = forall a. HasType a =&gt; k a -&gt; v a
</code></pre>

<p>My motivating example is a GADT-based representation of typed lambda calculus, and some of the functions I want to memoize do not fit the pattern.
After writing <a href="http://conal.net/blog/posts/memoizing-polymorphic-functions-part-one/" title="Blog post">part one</a>, I fooled around and found that I could transform these awkwardly typed polymorphic functions into isomorphic form that does indeed fit the restricted pattern of polymorphic types I can handle.</p>

<!--
**Edits**:

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

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

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

<h3>Awkward types</h3>

<p>The first awkwardly typed memoizee is the function application constructor:</p>

<pre><code>type AT = forall a b . (HasType a, HasType b) =&gt; E (a -&gt; b) -&gt; E a -&gt; E b

(:^) :: AT
</code></pre>

<p>Right away <code>AT</code> misses the required form.
It has two <code>HasType</code> constraints, and the first argument is parameterized over two type variables instead of one.
However, the second argument looks more promising, so let&#8217;s <code>flip</code> the arguments to get an isomorphic type:</p>

<pre><code>forall a b . (HasType a, HasType b) =&gt; E a -&gt; E (a -&gt; b) -&gt; E b
</code></pre>

<p>And then move the quantifier and constraint on <code>b</code> inside the outer (first) <code>-&gt;</code>:</p>

<pre><code>forall a . HasType a =&gt; E a -&gt; (forall b. HasType b =&gt; E (a -&gt; b) -&gt; E b)
</code></pre>

<p>We&#8217;re getting closer.
Next, define a <code>newtype</code> wrapper.</p>

<pre><code>newtype A2 a = A2 (forall b. HasType b =&gt; E (a -&gt; b) -&gt; E b)
</code></pre>

<p>So that <code>AT</code> is isomorphic to</p>

<pre><code>forall a . HasType a =&gt; E a -&gt; A2 a
</code></pre>

<p>i.e.,</p>

<pre><code>E :--&gt; A2
</code></pre>

<p>The function inside of <code>A2</code> doesn&#8217;t have the required form, but another <code>newtype</code> wrapper finishes the job.</p>

<pre><code>newtype EF a b = EF {unEF :: E (a -&gt; b) }

type H' a = EF a :--&gt; E

newtype H a = H { unH :: H' a }
</code></pre>

<p>The <code>AT</code> type is isomorphic <code>AP</code> where</p>

<pre><code>type AP = E :--&gt; H
</code></pre>

<h3>Curried memoization</h3>

<p>A &#8220;curried memo function&#8221; is one that takes one argument and produces another memo function.
For a simple memo function, not involving polymorphic recursion, there&#8217;s a simple recipe for curried memoization:</p>

<pre><code>memo2 :: (a -&gt; b -&gt; c) -&gt; (a -&gt; b -&gt; c)
memo2 f = memo (memo . f)
</code></pre>

<p>Our more polymorphic <code>memo</code> makes currying a little more awkward.
First, here&#8217;s a helper function for working <em>inside</em> of the representation of an <code>H</code>:</p>

<pre><code>inH :: (H' a -&gt; H' a) -&gt; (H a -&gt; H a)
inH h z = H (h (unH z))
</code></pre>

<p>The following more elegant definition doesn&#8217;t type-check, due to the rank 2 polymorphism:</p>

<pre><code>inH f = H . f . unH  -- type error
</code></pre>

<p>Now our <code>AP</code> memoizer is much like <code>memo2</code>:</p>

<pre><code>memoAP :: AP -&gt; AP
memoAP app' = memo (inH memo . app')
</code></pre>

<p>(A more general, consistent type for <code>memoAP</code> is <code>(f :--&gt; H) -&gt; (f :--&gt; H)</code>.)</p>

<h3>Isomorphisms</h3>

<p>Now, to define the isomorphisms.  Define</p>

<pre><code>toAP   :: AT -&gt; AP
fromAP :: AP -&gt; AT
</code></pre>

<p>The definitions:</p>

<pre><code>toAP app ea = H $  (EF eab) -&gt; app eab ea
fromAP app' eab ea = unH (app' ea) (EF eab)
</code></pre>

<p>If you erase the <code>newtype</code> wrappers &amp; unwrappers, you&#8217;ll see that <code>toAP</code> and <code>fromAP</code> are both just <code>flip</code>.</p>

<p>I constructed <code>fromAP</code> from the following specification:</p>

<pre><code>toAP (fromAP app') == app'
</code></pre>

<p>Transforming step-by-step into equivalent specifications:</p>

<pre><code> ea -&gt; H $  (EF eab) -&gt; (fromAP app') eab ea == app'

H $  (EF eab) -&gt; (fromAP app') eab ea == app' ea

 (EF eab) -&gt; (fromAP app') eab ea == unH (app' ea)

(fromAP app') eab ea == unH (app' ea) (EF eab)

fromAP app' eab ea == unH (app' ea) (EF eab)
</code></pre>

<h3>Memoizing vis isomorphisms</h3>

<p>Finally, I can memoize</p>

<pre><code>memoAT :: AT -&gt; AT
memoAT app = fromAP (memoAP (toAP app))
</code></pre>

<p>Again, a more elegant definition via <code>(.)</code> fails to type-check, due to rank 2 polymorphism.</p>

<p>The <code>Lam</code> (lambda abstraction) constructor can be handled similarly:</p>

<pre><code>Lam  :: (HasType a, HasType b) =&gt; V a -&gt; E b -&gt; E (a -&gt; b)
</code></pre>

<p>This time, no <code>flip</code> is needed.</p>

<h3>I wonder</h3>

<p>How far does this isomorphism trick go?</p>

<p>Is there an easier way to memoize polymorphic functions?</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=89&amp;md5=5153d8e78e7eabc69bf9e30c869f1ac2"><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-polymorphic-functions-part-two/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%2Fmemoizing-polymorphic-functions-part-two&amp;language=en_GB&amp;category=text&amp;title=Memoizing+polymorphic+functions+%26%238211%3B+part+two&amp;description=Part+one+of+this+series+introduced+the+problem+of+memoizing+functions+involving+polymorphic+recursion.+The+caching+data+structures+used+in+memoization+typically+handle+only+one+type+of+argument+at+a...&amp;tags=memoization%2Cpolymorphism%2Cblog" type="text/html" />
	</item>
		<item>
		<title>Memoizing polymorphic functions &#8211; part one</title>
		<link>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-one</link>
		<comments>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-one#comments</comments>
		<pubDate>Thu, 11 Jun 2009 00:36:34 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[polymorphism]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=88</guid>
		<description><![CDATA[Memoization takes a function and gives back a semantically equivalent function that reuses rather than recomputes when applied to the same argument more than once. Variations include not-quite-equivalence due to added strictness, and replacing value equality with pointer equality. Memoization is often packaged up polymorphically: memo :: (???) =&#62; (k -&#62; v) -&#62; (k -&#62; [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Memoizing polymorphic functions - part one
Tags: memoization, polymorphism

URL: http://conal.net/blog/posts/memoizing-polymorphic-functions-part-one/

-->

<!-- references -->

<!-- teaser -->

<p>Memoization takes a function and gives back a semantically equivalent function that reuses rather than recomputes when applied to the same argument more than once.
Variations include <a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/#comment-10166">not-quite-equivalence due to added strictness</a>, and replacing value equality with pointer equality.</p>

<p>Memoization is often packaged up polymorphically:</p>

<pre><code>memo :: (???) =&gt; (k -&gt; v) -&gt; (k -&gt; v)
</code></pre>

<p>For pointer-based (&#8220;lazy&#8221;) memoization, the type constraint (&#8220;???&#8221;) is empty.
For equality-based memoization, we&#8217;d need at least <code>Eq k</code>, and probably <code>Ord k</code> or <a href="http://conal.net/blog/posts/elegant-memoization-with-functional-memo-tries/" title="blog post"><code>HasTrie k</code></a> for efficient lookup (in a finite map or a possibly infinite <a href="http://conal.net/blog/tag/memoization/">memo trie</a>).</p>

<p>Although <code>memo</code> is polymorphic, its argument is a <em>monomorphic</em> function.
Implementations that use maps or tries exploit that monomorphism in that they use a type like <code>Map k v</code> or <code>Trie k v</code>.
Each map or trie is built around a particular (monomorphic) type of keys.
That is, a single map or trie does not mix keys of different types.</p>

<p>Now I find myself wanting to memoize <em>polymorphic</em> functions, and I don&#8217;t know how to do it.</p>

<!--
**Edits**:

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

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

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

<h3>Flavors of polymorphism</h3>

<p>If a recursively defined function <code>f</code> is polymorphic, then the recursion may still be monomorphic.
That is, recursive calls may be restricted to the same type instance as the parent call.
Most recursive polymorphic functions fit this form, because most polymorphic recursive data types are &#8220;regular&#8221;, meaning that a polymorphic data type is included in itself only at the same type instance.
For instance, the usual polymorphic lists and trees are regular.</p>

<h3>Example: GADTs</h3>

<p>Among other places, non-regular, or &#8220;<a href="ftp://ftp.kestrel.edu/pub/papers/meertens/nest5.ps">nested data types</a>&#8221; arise in statically typed encodings of typed languages.
For instance, here&#8217;s a GADT (generalized algebraic data type) for typed lambda calculus expressions:</p>

<pre><code>-- Variables
data V a = V String (Type a)

-- Expressions
data E :: * -&gt; * where
  Lit  :: a -&gt; E a                      -- literal
  Var  :: V  a -&gt; E a                   -- variable
  (:^) :: E (a -&gt; b) -&gt; E a -&gt; E b      -- application
  Lam  :: V a -&gt; E b -&gt; E (a -&gt; b)      -- abstraction
</code></pre>

<p>The <code>Type</code> type is sort of like <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Typeable.html#v:TypeRep"><code>TypeRep</code></a>, except that it is statically typed.</p>

<pre><code>data Type :: * -&gt; * where
  Bool   :: Type Bool
  Float  :: Type Float
  ...
  (:*:)  :: Type a -&gt; Type b -&gt; Type (a,b)
  (:-&gt;:) :: Type a -&gt; Type b -&gt; Type (a-&gt;b)
</code></pre>

<p>These GADTs (<code>E</code> and <code>Type</code>) are both non-regular, and so recursive functions over them will involve more than one argument type.</p>

<p>So how can we memoize?</p>

<h3>A first try</h3>

<p>Let&#8217;s consider a specific case of polymorphic functions:</p>

<pre><code>type k :--&gt; v = ∀ a. HasType a =&gt; k a -&gt; v a
</code></pre>

<p><code>HasType</code> is to <code>Typeable</code> as <code>Type</code> is to <code>TypeRep</code>.
The <code>memo</code> implementation I&#8217;m playing with relies on <code>HasType</code>.</p>

<p>The memoizer can have type</p>

<pre><code>memo :: (k :--&gt; v) -&gt; (k :--&gt; v)
</code></pre>

<p>which uses rank 2 polymorphism (because of the argument type&#8217;s <code>∀</code>, which cannot be moved to the outside).</p>

<p>My implementation of <code>memo</code> is similar to the discussion in <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.1948" title="Paper by Simon Peyton Jones, Simon Marlow, and Conal Elliott">Stretching the storage manager: weak pointers and stable names in Haskell</a></em>, but modernized a bit and adapted for polymorphism.
It uses an <a href="http://hackage.haskell.org/packages/archive/containers/0.2.0.1/doc/html/Data-IntMap.html"><code>IntMap</code></a> of lists of pairs of stable names (akin to pointers) for arguments and values for results.
The idea is to first use <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/System-Mem-StableName.html#v:hashStableName"><code>hashStableName</code></a> to get an <code>Int</code> to use as an <code>IntMap</code> key, and then linearly traverse the resulting list of binding pairs, comparing stable keys for equality.
(<code>StableName</code> has <code>Eq</code> but not <code>Ord</code>.)
Although <code>hashStableName</code> can map different stable names to the same hash value, collisions are rare, so the <code>StableBind</code> lists rarely have more than one element and the linear search is cheap.</p>

<pre><code>type SM k v = I.IntMap [StableBind k v]  -- Stable map

data StableBind k v = ∀ a. HasType a =&gt; SB (StableName (k a)) (v a)
</code></pre>

<p>The reason for hiding the type parameter <code>a</code> in <code>StableName</code> is so that different bindings can be for different types.</p>

<p>The key tricky bit is managing static typing while searching for a particular <code>StableName a</code> in a <code>[StableBind]</code>.
Here&#8217;s my implementation:</p>

<pre><code>blookup :: ∀ k v a. HasType a =&gt;
           StableName (k a) -&gt; [StableBind k v] -&gt; Maybe (v a)
blookup stk = look
 where
   look :: [StableBind k v] -&gt; Maybe (v a)
   look [] = Nothing
   look (SB stk' v : binds') 
     | Just Refl &lt;- tya `tyEq` typeOf2 stk', stk == stk' = Just v
     | otherwise                                         = look binds'
   tya :: Type a
   tya = typeT
</code></pre>

<p>The crucial magic bit is</p>

<pre><code>tyEq :: Type a -&gt; Type b -&gt; Maybe (a :=: b)
</code></pre>

<p>where <code>a :=: b</code> represents a proof that the types <code>a</code> and <code>b</code> are the same type.
The proof type has a simple GADT representation:</p>

<pre><code>data (:=:) :: * -&gt; * -&gt; * where Refl :: a :=: a
</code></pre>

<p>This simple type definition ensures that only valid type-equality proofs can exist.
Well, except for ⊥.
The guard&#8217;s pattern match with <code>Just Refl</code> will force evaluation, so that ⊥ can&#8217;t sneak by us.
That match also informs the type-checker that <code>stk</code> and <code>stk'</code> are stable names <em>of the same type</em> in that clause, which then makes <code>stk == stk'</code> be well-typed, and makes <code>Just v</code> have the required type, i.e., <code>Maybe (v a)</code>.</p>

<p>Finally, the <code>typeOf2</code> function is a simple helper that peels off two type constructors and extracts a <code>Type</code>:</p>

<pre><code>typeOf2 :: HasType a =&gt; g (f a) -&gt; Type a
</code></pre>

<h3>Wishing for more</h3>

<p>The type of <code>memo</code> above is too restrictive for my uses.
It only handles polymorphic functions of type <code>k a -&gt; v a</code>, and only with the single constraint <code>HasType a</code>.</p>

<p>The reason I want lazy memoization now is that I&#8217;m compressing expressions to maximize representation sharing, as John Hughes described in <em><a href="http://www.cse.chalmers.se/~rjmh/Papers/hughes_85_lazy.pdf" title="Paper by John Hughes">Lazy Memo-functions</a></em>.
Once sharing is maximized, pointer-based memoization works better, because equal values are pointer-equal.
To compress an expression, simply use a memoized copy function, as John suggested.</p>

<pre><code>compress :: HasType a =&gt; E a -&gt; E a
compress e = mcopy e
 where
   mcopy, copy :: HasType b =&gt; E b -&gt; E b
   -- Memo version
   mcopy = memo copy
   -- Copier, with memo-copied components
   copy (u :^ v)  = appM (mcopy u) (mcopy v)
   copy (Lam v b) = lamM v (mcopy b)
   copy e         = e
   -- memoized constructors
   appM :: (HasType a, HasType b) =&gt; E (a -&gt; b) -&gt; E a -&gt; E b
   appM = memo2 (:^)
   lamM :: (HasType a, HasType b) =&gt; V a -&gt; E b -&gt; E (a -&gt; b)
   lamM = memo2 Lam
</code></pre>

<p>The <code>memo2</code> function is defined in terms of <code>memo</code>, using some some <code>newtype</code> trickery.
Its type:</p>

<pre><code>memo2 :: HasType a =&gt;
         (k a -&gt; l a -&gt; v a) -&gt; (k a -&gt; l a -&gt; v a)
</code></pre>

<p>But, sigh, the <code>(:^)</code> and <code>Lam</code> constructors I&#8217;m trying to memoize do not have the required types.
My higher-order-polymorphic memo functions do not have flexible enough types.</p>

<p>And this is where I&#8217;m stuck.</p>

<p>I&#8217;d appreciate your ideas and suggestions.</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=88&amp;md5=5c6fe0b3b2071be4213b1ff342689402"><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-polymorphic-functions-part-one/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%2Fmemoizing-polymorphic-functions-part-one&amp;language=en_GB&amp;category=text&amp;title=Memoizing+polymorphic+functions+%26%238211%3B+part+one&amp;description=Memoization+takes+a+function+and+gives+back+a+semantically+equivalent+function+that+reuses+rather+than+recomputes+when+applied+to+the+same+argument+more+than+once.+Variations+include+not-quite-equivalence+due+to...&amp;tags=memoization%2Cpolymorphism%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>
		<item>
		<title>Composing memo tries</title>
		<link>http://conal.net/blog/posts/composing-memo-tries</link>
		<comments>http://conal.net/blog/posts/composing-memo-tries#comments</comments>
		<pubDate>Thu, 16 Oct 2008 02:18:12 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[memoization]]></category>
		<category><![CDATA[trie]]></category>
		<category><![CDATA[type class]]></category>
		<category><![CDATA[type class morphism]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=55</guid>
		<description><![CDATA[The post Elegant memoization with functional memo tries showed a simple type of search tries and their use for functional memoization of functions. This post provides some composition tools for memo tries, whose definitions are inevitable, in that they are determined by the principle presented in Simplifying semantics with type class morphisms. Compositional semantics and [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Composing memo tries

Tags: memoization, trie, type class, type class morphism

URL: http://conal.net/blog/posts/composing-memo-tries/

-->

<!-- references -->

<!-- teaser -->

<p>The 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> showed a simple type of search tries and their use for functional memoization of functions.
This post provides some composition tools for memo tries, whose definitions are <em>inevitable</em>, in that they are determined by the principle presented in <em><a href="http://conal.net/blog/posts/simplifying-semantics-with-type-class-morphisms" title="blog post">Simplifying semantics with type class morphisms</a></em>.</p>

<!--
**Edits**:

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

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

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

<h3>Compositional semantics and type class morphisms</h3>

<p>The discipline of denotational semantics defines meaning functions <em>compositionally</em>, i.e., the meaning of a construct must be some function of the meanings of its components.</p>

<p><a href="http://conal.net/blog/posts/simplifying-semantics-with-type-class-morphisms" title="blog post">Type class morphisms</a> make the denotational discipline even more specific:</p>

<blockquote>
  <p>The meaning of each method corresponds to the same method for the meaning.</p>
</blockquote>

<p>For instance,</p>

<pre><code>meaning (a `mappend` b) == meaning a `mappend` meaning b
</code></pre>

<h3>Memo tries, semantics, and morphisms</h3>

<p>The semantic function for a memo trie is <code>untrie</code>, which converts a trie (back) to a function:</p>

<pre><code>untrie :: (a :-&gt;: b) -&gt; (a  -&gt;  b)
</code></pre>

<p>Let&#8217;s look at the consequences of requiring that <code>untrie</code> be a morphism over <code>Monoid</code>, <code>Functor</code>, <code>Applicative</code>, <code>Monad</code>, <code>Category</code>, and <code>Arrow</code>, i.e.,</p>

<pre><code>untrie mempty          == mempty
untrie (s `mappend` t) == untrie s `mappend` untrie t

untrie (fmap f t)      == fmap f (untrie t)

untrie (pure a)        == pure a
untrie (tf &lt;*&gt; tx)     == untrie tf &lt;*&gt; untrie tx

untrie (return a)      == return a
untrie (u &gt;&gt;= k)       == untrie u &gt;&gt;= untrie . k

untrie id              == id
untrie (s . t)         == untrie s . untrie t

untrie (arr f)         == arr f
untrie (first t)       == first (untrie t)
</code></pre>

<p>These morphism properties imply that all of the expected laws hold, assuming that we interpret equality semantically (or observationally).
For instance,</p>

<pre><code>untrie (mempty `mappend` a)
  == untrie mempty `mappend` untrie a
  == mempty `mappend` untrie a
  == untrie a

untrie (fmap f (fmap g a))
  == fmap f (untrie (fmap g a))
  == fmap f (fmap g (untrie a))
  == fmap (f.g) (untrie a)
  == untrie (fmap (f.g) a)
</code></pre>

<h3>Deriving instances</h3>

<p>The implementation instances follow from applying <code>trie</code> to both sides of each of these morphism laws, using the property <code>trie . untrie == id</code>.</p>

<pre><code>instance (HasTrie a, Monoid b) =&gt; Monoid (a :-&gt;: b) where
  mempty        = trie mempty
  s `mappend` t = trie (untrie s `mappend` untrie t)

instance HasTrie a =&gt; Functor ((:-&gt;:) a) where
  fmap f t      = trie (fmap f (untrie t))

instance HasTrie a =&gt; Applicative ((:-&gt;:) a) where
  pure b        = trie (pure b)
  tf &lt;*&gt; tx     = trie (untrie tf &lt;*&gt; untrie tx)

instance HasTrie a =&gt; Monad ((:-&gt;:) a) where
  return a      = trie (return a)
  u &gt;&gt;= k       = trie (untrie u &gt;&gt;= untrie . k)

instance Category (:-&gt;:) where
  id            = trie id
  s . t         = trie (untrie s . untrie t)

instance Arrow (:-&gt;:) where
  arr f         = trie (arr f)
  first t       = trie (first (untrie t))
</code></pre>

<p>Correctness of these instances follows by applying <code>untrie</code> to each side of each definition and using the property <code>untrie . trie == id</code>.</p>

<p>The <code>Category</code> and <code>Arrow</code> instances don&#8217;t quite work, however, because of necessary but disallowed <code>HasTrie</code> constraints on the domain type.
<a href="http://www.cse.chalmers.se/~rjmh/" title="Home page of John Hughes">John Hughes</a> pointed out a similar problem near the end of <em><a href="http://citeseer.ist.psu.edu/hughes98generalising.html" title="Paper by John Hughes, appearing Science of Computer Programming, 2000">Generalising Monads to Arrows</a></em>, saying &#8220;I consider this to be a defect of the Haskell type system, which hopefully can be corrected in a future version of the language.&#8221;</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=55&amp;md5=be1c52f1c6ee6a99c5bb04b48f7debf4"><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/composing-memo-tries/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%2Fcomposing-memo-tries&amp;language=en_GB&amp;category=text&amp;title=Composing+memo+tries&amp;description=The+post+Elegant+memoization+with+functional+memo+tries+showed+a+simple+type+of+search+tries+and+their+use+for+functional+memoization+of+functions.+This+post+provides+some+composition+tools+for...&amp;tags=memoization%2Ctrie%2Ctype+class%2Ctype+class+morphism%2Cblog" type="text/html" />
	</item>
	</channel>
</rss>
