TChan字符串的序列化

时间:2012-06-23 21:51:59

标签: haskell

我已声明以下内容

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 class的成员。

对于TMapTPSQ我想知道是否有办法直接显示TVar中的值(因为它不会被更改,所以不需要锁定它)无需声明执行readTVar的实例?

1 个答案:

答案 0 :(得分:4)

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

只有一种方法可以从TVar中提取值,并且readTVar

readTVar :: TVar a -> STM a

...您可以使用IOatomically monad中执行此操作:

atomically . readTVar :: TVar a -> IO a
但是,

TChan更棘手,因为你不能在不清除整个TChan的情况下检查内容。通过将整个内容作为单个STM操作进行检查,然后将其全部重新插入,这是可行的,即使是浪费的。如果您选择这样做,最终还是需要在IO monad中运行。

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

但是,您没有必要使用IO类。您可以定义一个自定义函数来序列化Show monad中的数据类型。此外,由于以下情况,通常不建议将PSQ用于序列化目的:

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

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

但是,即使这些库只接受纯转换的实例而不接受TVar monad中的操作,所以你必须做的是将序列化分为两个步骤:

  1. IO monad中提取cereal的内容。
  2. 使用binary / Binary序列化内容(以及其他数据类型)。
  3. 还有最后一个警告,即并非所有数据类型都有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)
    

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

    entireTChan

    我省略了TChan的实现,它基本上会刷新entireTChan :: TChan a -> STM [a] 以检查整个内容然后重新加载它,但它的类型签名将是这样的:

    binary

    我也省略了反序列化实现,但我想如果您理解上面的示例并花时间学习如何使用cereal或{{1}}包,那么您应该能够解决它很容易。