1

我已经声明了以下内容

type KEY = (IPv4, Integer)
type TPSQ = TVar (PSQ.PSQ KEY POSIXTime)
type TMap = TVar (Map.Map KEY [String])

data Qcfg = Qcfg { qthresh :: Int, tdelay :: Rational, cwpsq :: TPSQ, cwmap :: TMap, cw
chan :: TChan String } deriving (Show)

并且希望这在某种意义上是可序列化的,即 Qcfg 可以写入磁盘或通过网络发送。当我编译这个我得到错误

No instances for (Show TMap, Show TPSQ, Show (TChan String))
      arising from the 'deriving' clause of a data type declaration
    Possible fix:
      add instance declarations for
      (Show TMap, Show TPSQ, Show (TChan String))
      or use a standalone 'deriving instance' declaration,
           so you can specify the instance context yourself
    When deriving the instance for (Show Qcfg)

我现在不太确定是否有机会序列化我的 TChan,尽管其中的所有单个节点都是 show 类的成员。

对于TMap并且TPSQ我想知道是否有方法可以TVar直接显示值(因为它不会被更改,所以不需要锁定它)而不必声明一个执行readTVar?

4

1 回答 1

4

我理解您的评论意味着您想要序列化的内容TVar不是TVar本身。

只有一种方法可以从 a 中提取值TVar,那就是readTVar

readTVar :: TVar a -> STM a

...您可以在IOmonad 中使用atomically

atomically . readTVar :: TVar a -> IO a

TChan但是,更棘手的是,因为您无法在不清除整个TChan. 这是可行的,即使是浪费,通过检查整个内容作为一个单一的STM动作,然后重新插入它们。如果这是您选择做的,它最终也需要在IOmonad 中运行。

这意味着您将无法为其派生Show实例,因为Show需要一个将其转换为 a 的纯计算String,而不是驻留在IOmonad 中的一个。

但是,您没有理由必须使用Show该类。您可以只定义一个自定义函数来序列化IOmonad 中的数据类型。此外,通常不建议将其Show用于序列化目的,因为:

  • 您的某些数据类型(如PSQ)没有Read实例
  • Read通常定义实例是一件很痛苦的事
  • 字符串表示非常节省空间

因此,我建议您使用适当的序列化库,例如binarycereal进行序列化和反序列化。这些将数据类型转换为二进制表示,它们使定义编码器和解码器变得非常容易。

但是,即使这些库也只接受纯转换的实例,而不接受IOmonad 中的操作,所以您必须做的是将您的序列化分解为两步过程:

  1. 在monad中提取TVars的内容。IO
  2. cereal使用/序列化内容(以及其他数据类型)binary

还有最后一个警告,即并非所有数据类型都有Binary实例(假设我们使用binary包),但幸运的是列表确实有一个Binary实例,因此一个方便的解决方法是将您的数据类型转换为列表(使用toList并序列化列表。然后,当您反序列化列表时,您可以使用它fromList来恢复原始类型。

所以下面的函数将完成所有这些(使用binary):

serializeQcfg file (Qcfg qthresh tdelay cwpsq cwmap cwchan) = do
    -- Step 1: Extract contents of concurrency variables
    psq    <- atomically $ readTVar cwpsq
    myMap  <- atomically $ readTVar cwmap
    myChan <- atomically $ entireTChan cwchan
    -- Step 2: Encode the extracted data
    encodeFile file (qthresh, tdelay, toList psq, myMap, myChan)

编辑:实际上,正如丹尼尔指出的那样,将所有原子事务组合成一个事务可能会更好,所以你实际上会这样做:

serializeQcfg file (Qcfg qthresh tdelay cwpsq cwmap cwchan) = do
    -- Step 1: Extract contents of concurrency variables
    (psq, myMap, myChain) <- atomically $ (,,) <$> readTVar cwpsq
                                               <*> readTVar cwmap
                                               <*> entireTChan cwchan
    -- Step 2: Encode the extracted data
    encodeFile file (qthresh, tdelay, toList psq, myMap, myChan)

我省略了 的实现entireTChan,它基本上会刷新TChan以检查整个内容,然后再次重新加载它,但它的类型签名将类似于:

entireTChan :: TChan a -> STM [a]

我也省略了反序列化实现,但我认为如果你理解上面的例子并花时间学习如何使用binaryorcereal包,你应该能够很容易地弄清楚它。

于 2012-06-24T16:28:24.387 回答