<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><br><div><div>On Nov 11, 2010, at 1:42 PM, Stephen Tetley wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; ">[*] In case anyone looks up MacGuffin on Wikipedia, I don't think the<br>description there is strictly accurate. A MacGuffin doesn't drive the<br>plot so much as throw the viewer of the scent.</span></blockquote></div><br><div>I think Hitchcock might disagree with you.</div><div><br></div><div>In any case, serializing functions is as easy as you want it to be. &nbsp;But, there is a big caveat: &nbsp;You are basically embedding a compiler into your run-time. &nbsp;It can be pretty minimal, relying only on facts known about recursively enumerable functions:</div><div><br></div><div><font class="Apple-style-span" face="Courier">class Serialize a where&nbsp;</font></div><div><font class="Apple-style-span" face="Courier"></font><font class="Apple-style-span" face="Courier">&nbsp;&nbsp; &nbsp; &nbsp;serialize </font><span class="Apple-tab-span" style="white-space:pre"><font class="Apple-style-span" face="Courier">        </font></span><font class="Apple-style-span" face="Courier">:: a -&gt; ByteString&nbsp;</font></div><div><font class="Apple-style-span" face="Courier"></font><font class="Apple-style-span" face="Courier">&nbsp;&nbsp; &nbsp; &nbsp;unSerialize :: ByteString -&gt; Maybe a <span class="Apple-style-span" style="white-space: pre;">&nbsp;</span></font><font class="Apple-style-span" face="Courier">-- Parsers can fail</font></div><div><font class="Apple-style-span" face="Courier"><br></font></div><div><font class="Apple-style-span" face="Courier">instance (Serialize a) =&gt; Serialize [a] where ...</font></div><div><font class="Apple-style-span" face="Courier">instance (Serialize a, Serialize b) =&gt; Serialize (a, b) where ...</font></div><div><font class="Apple-style-span" face="Courier"><br></font></div><div><font class="Apple-style-span" face="Courier">-- We can conclude that a and b must be enumerable from the requirement that</font></div><div><font class="Apple-style-span" face="Courier">-- f is recursively enumerable:</font></div><div><span class="Apple-style-span" style="font-family: Courier; ">instance (Serialize a, Enum a, Serialize b, Enum b) =&gt; Serialize (a -&gt; b) where</span></div><div><span class="Apple-tab-span" style="white-space:pre"><font class="Apple-style-span" face="Courier">        </font></span><font class="Apple-style-span" face="Courier">serialize f = serialize $ ( zip &nbsp; &nbsp; &nbsp; &nbsp; [minBound..maxBound]&nbsp;</font></div><div><span class="Apple-tab-span" style="white-space:pre"><font class="Apple-style-span" face="Courier">                                        </font></span><font class="Apple-style-span" face="Courier">(fmap f [minBound..maxBound]) )</font></div><div><font class="Apple-style-span" face="Courier">-- A map instance could be better: &nbsp;we trade some serialization time for more</font></div><div><font class="Apple-style-span" face="Courier">-- deserialization time. &nbsp;</font></div><div><font class="Apple-style-span" face="Courier">instance (Serialize a, Serialize b) =&gt; Serialize (Map a b) where ...</font></div><div><font class="Apple-style-span" face="Courier"><br></font></div><div><font class="Apple-style-span" face="Courier">instance (Serialize a, Serialize b) =&gt; Serialize (a -&gt; b) where</font></div><div><font class="Apple-style-span" face="Courier"><span class="Apple-tab-span" style="white-space:pre">        </span>serialize f = serialize . fromList $&nbsp;</font><span class="Apple-style-span" style="font-family: Courier; ">( zip &nbsp; &nbsp; &nbsp; &nbsp; [minBound..maxBound]&nbsp;</span></div><div><span class="Apple-tab-span" style="white-space: pre; "><font class="Apple-style-span" face="Courier">                                                   </font></span><font class="Apple-style-span" face="Courier">(fmap f [minBound..maxBound]) )</font></div><div><font class="Apple-style-span" face="Courier"><span class="Apple-tab-span" style="white-space:pre">        </span>deserialize map = \x -&gt; lookup x (bytestring_decode_map map)</font></div><div><font class="Apple-style-span" face="Courier"><span class="Apple-tab-span" style="white-space:pre">                        </span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;where bytestring_decode_map = ...</font></div><div><font class="Apple-style-span" face="Courier"><br></font></div><div>There are potentially big problems with this approach: &nbsp;</div><div>(i) &nbsp; Many data types are not instances of Enum (though the really should be. &nbsp;Deriving enumerations is not that hard. &nbsp;I am willing to take one for the team to get GHC to figure out how to derive Enum on arbitrary types, but I'm not sure where to start. &nbsp;Little help?)</div><div>(ii) &nbsp;Time and memory. &nbsp;We're not encoding a series of instructions for computing pure functions, but we're precomputing the results and saving them all for later. &nbsp;This is at least O(size of the domain) * O(of the function on each element). &nbsp;Not big in theoretical terms, but something like that could easily cause a factor of [1, \infty) &nbsp;slowdown in real code.</div><div>(iii) &nbsp;Serialized objects must be pure. &nbsp;In particular, you can't serialize general IO actions. &nbsp;I see this as a plus. &nbsp;It is still easy to write an algebra of serializable tokens and a non-serializable interpreter to generate IO actions from the tokens. &nbsp;We do this kind of thing all the time -- we just don't serialize the tokens usually.</div><div><br></div><div>I think (ii) is the biggest problem. &nbsp;And it is a big one. &nbsp;We basically need something like template haskell for runtime systems in order to do quasi-quoting and compilation at run-time so we can avoid reifying the domain and its image under f.</div><div><br></div><div>The only thing that can serialize an (IO a) is the Haskell runtime, and it does it by running the action (and so putting its sub-steps in a series).&nbsp;</div></body></html>