Suppose you have a serializer/deserializer type class
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
and it turns out that it's crucial to have a special helper function for each type a
, e.g.
compress :: ByteString -> ByteString -- actually varies with the original type
I see compress
as a function that I would like to associate with each a
that is a SerDes
. (The word "associate" is probably a bad choice, and the reason why internet searches yield nothing.)
The example is not as contrived as it looks, for example when decompress
is an optional
feature of the serializer/deserializer. (Yes, the helper could be avoided by augmenting
ser
with a switch that controls the compression, ser:: a -> Bool -> ByteString
, or better use a Config
record. But let's stick with the example.)
One way to do this is a 'dummy' class, a singleton:
data For a = For
Then this will work:
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: For a -> ByteString -> ByteString
and a compress
for a
would be instantiated as
compress (For :: For MyType) input = ...
Another way, somewhat unusual, would be to stick all the functions in a record.
data SerDes a = SerDes { ser :: a -> ByteString
, des :: ByteString -> a
, compress :: ByteString -> ByteString
}
Are there any other ways to "associate" the compress
function with the type a
?
TypeApplications
are the way to do this sort of thing in modern Haskell, whereas proxies / tagged values are merely of historical interest. (In factTagged
is probably just obselete, it never was very popular; I used to prefer it over proxies while TypeApplications weren't available, but now I'd never use it anymore. Proxies can be combined with TypeApplications better, simply passing(Proxy @a)
.) – Marlite