<div class="gmail_quote">On Tue, Nov 18, 2008 at 11:18 AM, Brent Yorgey <span dir="ltr">&lt;<a href="mailto:byorgey@seas.upenn.edu">byorgey@seas.upenn.edu</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hm, interesting! &nbsp;The problem is that &#39;get&#39; does not take any<br>
arguments, so must determine what to do from the type at which it is<br>
called. &nbsp;So the number of words to be read needs to be in the type.<br>
We can&#39;t put actual Int values in a type -- but there is actually a<br>
way to do what you want, by encoding natural numbers at the type<br>
level! &nbsp;I don&#39;t know whether this really belongs on a &#39;beginners&#39; list<br>
but I couldn&#39;t resist. =)<br>
<br>
<br>
data Z &nbsp; &nbsp; &nbsp;-- the type representing zero<br>
data S n &nbsp; &nbsp;-- the type representing the successor of another natural<br>
<br>
-- for example, &nbsp;Z, &nbsp;S Z, &nbsp;and S (S Z) &nbsp;are types representing<br>
-- &nbsp; zero, one, and two.<br>
<br>
-- the n is for a type-level natural representing the length of the list.<br>
data MFChar n = MFChar [Word8]<br>
<br>
-- add a Word8 to the beginning of an MFChar, resulting in an MFChar<br>
-- &nbsp; one word longer<br>
mfCons :: Word8 -&gt; MFChar n -&gt; MFChar (S n)<br>
mfCons w (MFChar ws) = MFChar (w:ws)<br>
<br>
instance Binary (MFChar Z) where<br>
 &nbsp;get = return $ MFChar []<br>
<br>
instance (Binary (MFChar n)) =&gt; Binary (MFChar (S n)) where<br>
 &nbsp;get = do ebcdic &lt;- getWord8<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rest &nbsp; &lt;- get &nbsp; &nbsp;-- the correct type of get is<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-- inferred due to the use of mfCons below<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return $ mfCons (ebcdicToAscii ebcdic) rest<br>
<br>
<br>
Now if you wanted to read a field with 20 chars, you can use<br>
<br>
 &nbsp;get :: Get (MFChar (S (S (S ... 20 S&#39;s ... Z))))<br>
<br>
Ugly, I know. You could make it slightly more bearable by defining<br>
some type synonyms at the top of your program like<br>
<br>
 &nbsp;type Five = S (S (S (S (S Z))))<br>
 &nbsp;type Ten = S (S (S (S (S Five))))<br>
<br>
and so on. &nbsp;Then you can just say &nbsp; get :: Get (MFChar Ten) or whatever.<br>
<br>
This is untested but it (or something close to it) ought to work. &nbsp;Of<br>
course, you may well ask yourself whether this contortion is really<br>
worth it. &nbsp;Maybe it is, maybe it isn&#39;t, but I can&#39;t think of a better<br>
way to do it in Haskell. &nbsp;In a dependently typed language such as<br>
Agda, we could just put regular old natural numbers in the types,<br>
instead of going through contortions to encode natural numbers as<br>
types as we have to do here. &nbsp;So I guess the real answer to your<br>
question is &quot;use a dependently typed language&quot;. =)<br>
<br>
If you have problems getting this to work or more questions, feel free<br>
to ask!<br>
</blockquote></div><br>Very interesting solution to the problem. I tried it out and it works perfectly... but it&#39;s just too much of a hack for my tastes (no offense; I think it was very cool). I thought about it a bit and realized what I really want is a way to deal with tuples of the same type, which led to this kind of implementation.<br>
<br><div style="margin-left: 40px;">class RepTuple a b | a -&gt; b where<br>&nbsp;&nbsp;&nbsp; toList :: a -&gt; [b]<br>&nbsp;&nbsp;&nbsp; tMap :: (b -&gt; b) -&gt; a -&gt; a<br><br>instance RepTuple (a, a) a where<br>&nbsp;&nbsp;&nbsp; toList (a, b) = [a, b]<br>&nbsp;&nbsp;&nbsp; tMap f (a, b) = (f a, f b)<br>
</div><br>And so on and so forth for every kind of tuple. Of course, this runs into the issue of the single case, for which I used the OneTuple library (actually, I wrote my own right now, but I intend to just use the OneTuple library).<br>
<br>I can then do something like this (which I have tested and works):<br><br><div style="margin-left: 40px;">data MFChar w = MFChar w<br>&nbsp;&nbsp;&nbsp; deriving Eq<br>instance (RepTuple w a, Integral a) =&gt; Show (MFChar w) where<br>
&nbsp;&nbsp;&nbsp; show (MFChar ws) = map (chr . fromIntegral) $ toList ws<br>instance (Integral a, Binary w, RepTuple w a) =&gt; Binary (MFChar w) where<br>&nbsp;&nbsp;&nbsp; put = undefined<br>&nbsp;&nbsp;&nbsp; get = do ebcdic &lt;- get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; let ascii = tMap ebcdicToAscii ebcdic<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $ MFChar ascii<br><br>type MFChar1 = MFChar (OneTuple Word8)<br>type MFChar2 = MFChar (Word8, Word8)<br>type MFChar4 = MFChar (Word8, Word8, Word8, Word8)<br>type MFChar5 = MFChar (Word8, Word8, Word8, Word8, Word8)<br>
type MFChar10 = MFChar (Word8, Word8, Word8, Word8, Word8,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Word8, Word8, Word8, Word8, Word8)<br></div><br>If I wanted, I could do away with the tMap function and just include the ebcdicToAscii step in the show instance.<br>
<br>Michael<br>