具有类型约束的GADT的Functor实例

时间:2013-06-17 21:59:13

标签: haskell functor type-constraints

今天我想调查是否有可能以这种方式构造数据类型,它不存储其类型签名类型的数据,而是它的另一种表示形式。所以,这是我尝试的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)无法推断。如何约束F​​unctor实例,以便Serialize中强制执行fmap

1 个答案:

答案 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而不是重复的解码和编码,就像您的情况一样。