<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	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/"
	
	>
<channel>
	<title>Comments on: Memoizing polymorphic functions &#8211; part two</title>
	<atom:link href="http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two/feed" rel="self" type="application/rss+xml" />
	<link>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two</link>
	<description>Inspirations &#38; experiments, mainly about denotative/functional programming in Haskell</description>
	<lastBuildDate>Sat, 26 Sep 2020 21:06:12 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.17</generator>
	<item>
		<title>By: Conal Elliott &#187; Blog Archive &#187; Memoizing polymorphic functions via unmemoization</title>
		<link>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two#comment-498</link>
		<dc:creator><![CDATA[Conal Elliott &#187; Blog Archive &#187; Memoizing polymorphic functions via unmemoization]]></dc:creator>
		<pubDate>Sat, 02 Oct 2010 15:41:07 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=89#comment-498</guid>
		<description><![CDATA[&lt;p&gt;[...] 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 [...]&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>[&#8230;] 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;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Neel Krishnaswami</title>
		<link>http://conal.net/blog/posts/memoizing-polymorphic-functions-part-two#comment-497</link>
		<dc:creator><![CDATA[Neel Krishnaswami]]></dc:creator>
		<pubDate>Thu, 18 Jun 2009 19:30:17 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=89#comment-497</guid>
		<description><![CDATA[&lt;p&gt;Hi Conal, I don&#039;t know if this is enough for your purpose, but there&#039;s a beautiful trick I learned from Janis Voigtlander&#039;s 2009 POPL paper &quot;Bidirectionalization for Free&quot;. The idea is to to exploit parametricity -- if you have a function that operates over lists parametrically, you can find out what it does on &lt;em&gt;any&lt;/em&gt; argument by passing it a list of unique integers, and looking at how it permutes those integers.&lt;/p&gt;

&lt;p&gt;The same trick ought to work for memoization. If we want to write a function&lt;/p&gt;

&lt;pre&gt;
     memo&#039; : (all a. k a -&gt; v a) -&gt; (all a. k a -&gt; v a)`
&lt;/pre&gt;

&lt;p&gt;then it&#039;s sufficient if we can write the memo function for the Int case. More abstactly, we can do this if k and v are functors, and k also has a way to distribute over the state monad (that is, we want a function &lt;code&gt;seq : (k (state a)) -&gt; (state (k a))&lt;/code&gt;. Then, we want to write something like (I&#039;ll use a mutant hybrid ML/Haskell syntax here):&lt;/p&gt;

&lt;pre&gt;
type state r a = (Int, Int -&gt; r) -&gt; ((Int, Int -&gt; r), a)

eat : r -&gt; state r int 
eat r (size, mapping) = ((size+1, n. if n = size then r else mapping n), size)

run : state r a -&gt; int -&gt; r -&gt; ((int, int -&gt; r), a)
run state initsize initmap = state (initsize, initmap)

seq : k (state r a) -&gt; state r (k a)
seq blah = whatever

memo&#039; (f : all a. k a -&gt; v a) = 
  let table : ref (k int -&gt; option (v int)) =  ref (x. Nothing)   -- allocate a table
  let memofun x : k b = 
    let leaves : k (state b int) = map_k eat x 
    let traversal : state b (k int) = seq leaves 
    let ((size, indices), shape) = run traversal 0 (x. bot)
    case !table shape of 
      Just result_shape -&gt; fmap indices result_shape
    &#124; Nothing -&gt; let result_shape = f shape in 
                 begin
                    table := update table shape result_shape;  -- imperatively update the memo table
                    return (fmap indices result_shape)
                 end
 
&lt;/pre&gt;

&lt;p&gt;The idea is that we use map and seq to construct an action that will walk over the key, replacing each polymorphic
value with a unique integer index. Map crawls over the term, and we stick a little command in a state monad to eat
the value there. Then, we use &lt;code&gt;seq&lt;/code&gt; to sequence all those terms, ensuring we a) get unique indices at all the leaves,
and b) we can get a mapping from those indices back to the original values.&lt;/p&gt;

&lt;p&gt;Then, we can use memoize the int version of the value, and its result. Given an integer-valued result, we can use the mapping from indices to values to put back the correct values. So v needs to be a functor, and k needs to be a functor with a sequencing distribution property -- the traversable stuff ought to be the right general property to ask for.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>Hi Conal, I don&#8217;t know if this is enough for your purpose, but there&#8217;s a beautiful trick I learned from Janis Voigtlander&#8217;s 2009 POPL paper &#8220;Bidirectionalization for Free&#8221;. The idea is to to exploit parametricity &#8212; if you have a function that operates over lists parametrically, you can find out what it does on <em>any</em> argument by passing it a list of unique integers, and looking at how it permutes those integers.</p>

<p>The same trick ought to work for memoization. If we want to write a function</p>

<pre>
     memo' : (all a. k a -&gt; v a) -&gt; (all a. k a -&gt; v a)`
</pre>

<p>then it&#8217;s sufficient if we can write the memo function for the Int case. More abstactly, we can do this if k and v are functors, and k also has a way to distribute over the state monad (that is, we want a function <code>seq : (k (state a)) -&gt; (state (k a))</code>. Then, we want to write something like (I&#8217;ll use a mutant hybrid ML/Haskell syntax here):</p>

<pre>
type state r a = (Int, Int -&gt; r) -&gt; ((Int, Int -&gt; r), a)

eat : r -&gt; state r int 
eat r (size, mapping) = ((size+1, n. if n = size then r else mapping n), size)

run : state r a -&gt; int -&gt; r -&gt; ((int, int -&gt; r), a)
run state initsize initmap = state (initsize, initmap)

seq : k (state r a) -&gt; state r (k a)
seq blah = whatever

memo' (f : all a. k a -&gt; v a) = 
  let table : ref (k int -&gt; option (v int)) =  ref (x. Nothing)   -- allocate a table
  let memofun x : k b = 
    let leaves : k (state b int) = map_k eat x 
    let traversal : state b (k int) = seq leaves 
    let ((size, indices), shape) = run traversal 0 (x. bot)
    case !table shape of 
      Just result_shape -&gt; fmap indices result_shape
    | Nothing -&gt; let result_shape = f shape in 
                 begin
                    table := update table shape result_shape;  -- imperatively update the memo table
                    return (fmap indices result_shape)
                 end
 
</pre>

<p>The idea is that we use map and seq to construct an action that will walk over the key, replacing each polymorphic
value with a unique integer index. Map crawls over the term, and we stick a little command in a state monad to eat
the value there. Then, we use <code>seq</code> to sequence all those terms, ensuring we a) get unique indices at all the leaves,
and b) we can get a mapping from those indices back to the original values.</p>

<p>Then, we can use memoize the int version of the value, and its result. Given an integer-valued result, we can use the mapping from indices to values to put back the correct values. So v needs to be a functor, and k needs to be a functor with a sequencing distribution property &#8212; the traversable stuff ought to be the right general property to ask for.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
