Today I wanted to investigate if it is possible to construct a data type in such a way, that it does not store the data of the type of its type signature, but another representation of it. So, here is my attempt of an GADT which has a type constructor of type a, but a data constructor of type ByteString.
{-# LANGUAGE GADTs #-}
import Data.ByteString.Char8
import Data.Serialize
data Serialized a where
MkSerialized :: (Serialize a) => ByteString -> Serialized a
Now I can define a decode' function in the following way:
decode' :: (Serialize a) => Serialized a -> a
decode' (MkSerialized bs) = let Right r = (decode bs) in r
And it works:
let s = MkSerialized (encode "test") :: Serialized String
print $ decode' s -- prints "test"
My problem is now that I'd like Serialized to be an instance of Functor.
instance Functor Serialized where
fmap f (MkSerialized bs) = MkSerialized (encode (f (right (decode bs))))
where right (Right r) = r
But I get the error (Serialize b) can not be deduced. How can I constraint the Functor instance so that Serialize is enforced in the fmap?
You can do this using a CoYoneda functor.
The idea is simple: have an additional functional field where you accumulate your fmaping functions. When you decode your value, then apply that function.
Here's the code:
{-# LANGUAGE GADTs #-}
import Data.ByteString.Char8
import Data.Serialize
data Serialized a where
MkSerialized
:: (Serialize a)
=> ByteString -> (a -> b) -> Serialized b
decode' :: Serialized a -> a
decode' (MkSerialized bs f) = let Right r = decode bs in f r
instance Functor Serialized where
fmap f (MkSerialized bs g) = MkSerialized bs (f . g)
This also has the benefit of automatically fusing multiple fmaps instead of repeated decodings and encodings, as would be in your case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With