我理解您的评论意味着您想要序列化的内容而TVar
不是TVar
本身。
只有一种方法可以从 a 中提取值TVar
,那就是readTVar
:
readTVar :: TVar a -> STM a
...您可以在IO
monad 中使用atomically
:
atomically . readTVar :: TVar a -> IO a
TChan
但是,更棘手的是,因为您无法在不清除整个TChan
. 这是可行的,即使是浪费,通过检查整个内容作为一个单一的STM
动作,然后重新插入它们。如果这是您选择做的,它最终也需要在IO
monad 中运行。
这意味着您将无法为其派生Show
实例,因为Show
需要一个将其转换为 a 的纯计算String
,而不是驻留在IO
monad 中的一个。
但是,您没有理由必须使用Show
该类。您可以只定义一个自定义函数来序列化IO
monad 中的数据类型。此外,通常不建议将其Show
用于序列化目的,因为:
- 您的某些数据类型(如
PSQ
)没有Read
实例
Read
通常定义实例是一件很痛苦的事
- 字符串表示非常节省空间
因此,我建议您使用适当的序列化库,例如binary
或cereal
进行序列化和反序列化。这些将数据类型转换为二进制表示,它们使定义编码器和解码器变得非常容易。
但是,即使这些库也只接受纯转换的实例,而不接受IO
monad 中的操作,所以您必须做的是将您的序列化分解为两步过程:
- 在monad中提取
TVar
s的内容。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]
我也省略了反序列化实现,但我认为如果你理解上面的例子并花时间学习如何使用binary
orcereal
包,你应该能够很容易地弄清楚它。