<?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; polymorphism</title>
	<atom:link href="http://conal.net/blog/tag/polymorphism/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 &#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>
	</channel>
</rss>
