4

Serialize次のデータ型のインスタンスを作成する必要があります。

data AnyNode = forall n . (Typeable n, Serialize n) => AnyNode n

これをシリアル化することは問題ありませんが、コンパイラは の特定のインスタンスを解決する方法がないため、逆シリアル化を実装できません。これは、 が外部スコープから分離されているためですSerialize nn

関連する議論が 2006 年にありました。今日、なんらかの解決策または回避策が到着したかどうか疑問に思っています。

4

3 に答える 3

9

シリアル化するときに型にタグを付け、逆シリアル化するときに辞書を使用して型のタグを解除します。エラーチェックなどを省略した擬似コードを次に示します。

serialAnyNode (AnyNode x) = serialize (typeOf n, serialize x)

deserialAnyNode s = case deserialize s of
          (typ,bs) -> case typ of
                         "String" -> AnyNode (deserialize bs :: String)
                         "Int" -> AnyNode (deserialize bs :: Int)
                         ....

関数で型の閉じたユニバースのみを逆シリアル化できることに注意してください。追加の作業を行うことで、タプル、maybes、および either などの派生型を逆シリアル化することもできます。

しかし、完全に新しい型の "Gotcha" 派生型Typeableandを宣言するとしたらSerializedeserialAnyNodeもちろん拡張なしでは対応できません。

于 2013-08-04T03:29:33.537 に答える
6

(情報から抽出された)実際のタイプでディスパッチできるように、逆シリアル化関数のある種の集中型「レジストリ」が必要ですTypeable。逆シリアル化するすべての型が同じモジュールにある場合、これは非常に簡単にセットアップできます。それらが複数のモジュールにある場合は、マッピングを持つ 1 つのモジュールが必要です。

型のコレクションがより動的で、コンパイル時に簡単に利用できない場合は、おそらく動的リンクを使用してデシリアライザーにアクセスできます。逆シリアル化する型ごとに、情報から派生した名前で C 呼び出し可能関数をエクスポートしTypeableます (TH を使用してこれらを生成できます)。次に、実行時に型をデシリアライズする場合は、同じ名前を生成し、動的リンカーを使用して関数のアドレスを取得し、FFI ラッパーを使用して Haskell 呼び出し可能関数を取得します。これはかなり複雑なプロセスですが、ライブラリにまとめることができます。いいえ、申し訳ありませんが、そのようなライブラリはありません。

于 2013-08-04T12:09:00.197 に答える
2

ここで何を求めているのかを正確に伝えるのは難しいです。確かに、特定の type を選択し、Ta を逆シリアルByteString化し、 に格納できAnyNodeます。AnyNodeしかし、それはユーザーにとってあまり良いことではありませんT。制約がなければTypeable、ユーザーは型が何であるかを知ることさえできません (Typeable混乱を招くので、制約を取り除きましょう)。たぶん、あなたが望むのは実存的なものではなく、普遍的なものです。

Serialize2 つのクラスに分割してRead、and と呼びShow、少し単純化します (たとえばread、失敗しないようにします)。

だから私たちは持っています

class Show a where show :: a -> String
class Read a where read :: String -> a

Show-able 値の存在コンテナを作成できます。

data ShowEx where
  ShowEx :: forall a. Show a => a -> ShowEx
-- non-GADT: data ShowEx = forall a. Show a => ShowEx a

しかし、もちろんShowExは と同形Stringなので、これを指している点はあまりありません。Readしかし、 is の存在論的意味はさらに少ないことに注意してください:

data ReadEx where
  ReadEx :: forall a. Read a => a -> ReadEx
-- non-GADT: data ReadEx = forall a. Read a => ReadEx a

私があなたに与えるとき、ReadExつまり∃a. Read a *> a、それはあなたが何らかの型の値を持っていて、その型が何であるかはわかりませんがString、同じ型の別の値に変換できることを意味します. しかし、あなたはそれで何もできません!readsしか生成 aしませんが、何が何であるかがわからない場合は、何の役にも立ちませんa

あなたが望むかもしれないReadのは、呼び出し元が選択できるタイプ、つまりユニバーサルです。何かのようなもの

newtype ReadUn where
  ReadUn :: (forall a. Read a => a) -> ReadUn
-- non-GADT: newtype ReadUn = ReadUn (forall a. Read a => a)

(のようReadExに、あなたは作ることができますShowUn-つまり∀a. Show a => a-そしてそれは同じように役に立たないでしょう。)

は本質的に --ie --ShowExの引数であり、本質的に --ie の戻り値であることに注意してください。showshow :: (∃a. Show a *> a) -> StringReadUnreadread :: String -> (∀a. Read a => a)

では、実存的なものと普遍的なもの、何を求めているのでしょうか? ∀a. (Show a, Read a) => a確かにまたはのようなものを作成できます∃a. (Show a, Read a) *> aが、ここでもあまり役に立ちません。本当の問題は数量詞です。

(私は少し前に別の文脈でこれについて話している質問をしました。)

于 2013-08-04T03:35:29.220 に答える