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)

個々のノードはすべて show クラスのメンバーですが、TChan をシリアル化する機会があるかどうかはよくわかりません。

TMapと私はTPSQ、値を直接表示する方法があるかどうか疑問に思いますTVar(変更されないため、ロックする必要はありません) ? を実行するインスタンスを宣言する必要はありませreadTVarん。

4

1 に答える 1

4

あなたのコメントは、それ自体ではなく、の内容をシリアライズしたいという意味だと理解しました。TVarTVar

から値を抽出する方法は 1 つしかありません。TVarそれは次のreadTVarとおりです。

readTVar :: TVar a -> STM a

IO...を使用してモナドで行うことができますatomically

atomically . readTVar :: TVar a -> IO a

TChanただし、全体をフラッシュしないと内容を検査できないため、より注意が必要ですTChanSTMこれは、内容全体を 1 回のアクションとして検査し、それらをすべて再挿入することによって、たとえ無駄があったとしても実行可能です。これを選択した場合、最終的にはIOモナドで実行する必要があります。

これは、モナドに存在するものではなく、それを に変換する純粋な計算を期待するShowため、そのインスタンスを派生させることができないことを意味します。ShowStringIO

Showただし、クラスを使用しなければならない理由はありません。IOカスタム関数を定義して、モナドでデータ型をシリアル化できます。Showまた、次の理由から、シリアル化の目的で使用することは一般的にお勧めできません。

  • 一部のデータ型 ( などPSQ) にはReadインスタンスがありません
  • Read一般的にインスタンスを定義するのは面倒です
  • 文字列表現は非常にスペース効率が悪い

binaryそのため、シリアライゼーションとデシリアライゼーションを行うために、またはのような適切なシリアライゼーション ライブラリを使用することをお勧めしますcereal。これらはデータ型をバイナリ表現に変換し、エンコーダーとデコーダーの定義を非常に簡単にします。

ただし、これらのライブラリでさえ、純粋な変換のインスタンスのみを受け入れ、IOモナドでの操作は受け入れないため、シリアライゼーションを 2 段階のプロセスに分解する必要があります。

  1. モナドTVarで s の内容を抽出します。IO
  2. cereal/を使用してコンテンツを (残りのデータ型と共に) シリアル化しますbinary

すべてのデータ型にインスタンスがあるわけではありません(パッケージBinaryを使用すると仮定します) が、幸いにもリストにはインスタンスがあるため、便利な回避策は、データ型をリストに変換することです。 (リストを使用してシリアル化します。次に、リストを逆シリアル化するときに、を使用して元の型を復元します。binaryBinarytoListfromList

したがって、次の関数はそのすべてを行います ( を使用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またはcerealパッケージの使用方法を学習すれば、十分に簡単に理解できるはずです。

于 2012-06-24T16:28:24.387 に答える