9

今天我想研究是否有可能以这样一种方式构造一个数据类型,它不存储其类型签名的类型的数据,而是存储它的另一种表示。所以,这是我尝试的 GADT,它具有 type 的类型构造函数a,但具有 type 的数据构造函数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

但是我得到错误(Serialize b)无法推断。如何约束 Functor 实例,以便Serializefmap?

4

1 回答 1

7

您可以使用CoYoneda 函子来做到这一点。

这个想法很简单:有一个额外的函数域,你可以在其中积累你的fmaping 函数。当您解码您的值时,然后应用该功能。

这是代码:

{-# 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)

这还具有自动融合多个fmaps 而不是重复解码和编码的好处,就像您的情况一样。

于 2013-06-19T07:01:59.700 回答