
On Wed, May 18, 2011 at 12:32 PM, Simon Meier
Hello Haskell-Cafe,
There are many providers of Writes. Each bounded-length-encoding of a standard Haskell value is likely to have a corresponding Write. For example, encoding an Int32 as a big-endian, little-endian, and host-endian byte-sequence is currently achieved with the following three functions.
writeInt32BE :: Write Int32 writeInt32LE :: Write Int32 writeInt32HE :: Write Int32
I would like to avoid naming all these encodings individually. Especially, as the situation becomes worse for more elaborate encodings like hexadecimal encodings. There, we encounter encodings like the utf8-encoding of the hexadecimal-encoding with lower-case letters of an Int32.
writeInt32HexLowerUtf8 :: Write Int32
I really don't like that. Therefore, I'm thinking about the following solution based on type-classes. We introduce a single typeclass
class Writable a where write :: Write a
and use a bunch of newtypes to denote our encodings.
newtype Ascii7 a = Ascii7 { unAscii7 :: a } newtype Utf8 a = Utf8 { unUtf8 :: a } newtype HexUpper a = HexUpper { unHexUpper :: a } newtype HexLower a = HexLower { unHexLower :: a } ...
Assuming FlexibleInstnaces, we can write encodings like the above hex-encoding as instances
instance Write (Utf8 (HexLower Int32)) where write = ...
This composes rather nicely and allows the implementations to exploit special properties of the involved data. For example, if we also had a HTML escaping marker
newtype Html a = Html { unHtml :: a }
Then, the instance
instance Write (Utf8 (HTML (HexLower Int32))) where write (Utf8 (HTML (HexLower i))) = write (Utf8 (HexLower i))
If I were authoring the above code, I don't see why that code is any easier to write or easier to read than:
urf8HtmlHexLower i = utf8HexLower i
And if I were using the encoding functions, I would much prefer to see:
urf8HtmlHexLower magicNumber
In my code, instead of:
write $ Utf8 $ HTML $ HexLower magicNumber
In addition, this would be difficult for me as a developer using the proposed library, because I would have no way to know which combinations of newtypes are valid from reading the haddocks. Maybe I'm missing something fundamental, but this approach seems more cumbersome to me as a library author (more boilerplate) and as the user of the library (less clarity in the docs and in the resultant code). Antoine