今天我想调查是否有可能以这种方式构造数据类型,它不存储其类型签名类型的数据,而是它的另一种表示形式。所以,这是我尝试的GADT,它有一个a
类型的类型构造函数,但是类型为ByteString
的数据构造函数。
{-# LANGUAGE GADTs #-}
import Data.ByteString.Char8
import Data.Serialize
data Serialized a where
MkSerialized :: (Serialize a) => ByteString -> Serialized a
现在我可以通过以下方式定义decode'
函数:
decode' :: (Serialize a) => Serialized a -> a
decode' (MkSerialized bs) = let Right r = (decode bs) in r
它有效:
let s = MkSerialized (encode "test") :: Serialized String
print $ decode' s -- prints "test"
我的问题是,我希望Serialized
成为Functor
的实例。
instance Functor Serialized where
fmap f (MkSerialized bs) = MkSerialized (encode (f (right (decode bs))))
where right (Right r) = r
但我得到错误(序列化b)无法推断。如何约束Functor实例,以便Serialize
中强制执行fmap
?
答案 0 :(得分:6)
您可以使用 CoYoneda仿函数来完成此操作。
这个想法很简单:有一个额外的功能字段,您可以累积fmap
个功能。解码值时,请应用该函数。
以下是代码:
{-# 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)
这也有利于自动融合多个fmap
而不是重复的解码和编码,就像您的情况一样。