我理解您的评论意味着您想要序列化的内容而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通常定义实例是一件很痛苦的事
- 字符串表示非常节省空间
因此,我建议您使用适当的序列化库,例如binary或cereal进行序列化和反序列化。这些将数据类型转换为二进制表示,它们使定义编码器和解码器变得非常容易。
但是,即使这些库也只接受纯转换的实例,而不接受IOmonad 中的操作,所以您必须做的是将您的序列化分解为两步过程:
- 在monad中提取
TVars的内容。IO
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包,你应该能够很容易地弄清楚它。