反序列化存在数据类型

时间:2013-08-04 01:44:31

标签: haskell deserialization existential-type

我需要为以下数据类型编写Serialize实例:

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

序列化这个没问题,但我无法实现反序列化,因为编译器无法解析Serialize n的特定实例,因为n与外部范围隔离。 / p>

a related discussion in 2006。我现在想知道今天是否有任何解决方案或解决方法。

3 个答案:

答案 0 :(得分: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)
                         ....

请注意,您只能使用您的函数反序列化已关闭类型的Universe。通过一些额外的工作,您还可以反序列化派生类型,如元组,maybes和eithers。

但是,如果我要声明一个全新类型“Gotcha”派生TypeableSerializedeserialAnyNode当然无法在没有扩展的情况下处理它。

答案 1 :(得分:6)

您需要具有反序列化函数的某种集中式“注册表”,以便您可以调度实际类型(从Typeable信息中提取)。如果要反序列化的所有类型都在同一模块中,则很容易设置。如果它们在多个模块中,则需要有一个具有映射的模块。

如果您的类型集合更加动态且在编译时不易获得,您可以使用动态链接来访问反序列化器。对于要反序列化的每种类型,导出一个C可调用函数,其名称源自Typeable信息(您可以使用TH生成这些函数)。然后在运行时,当您想要反序列化类型时,生成相同的名称并使用动态链接器来获取函数的地址,然后使用FFI包装器来获取Haskell可调用函数。这是一个相当复杂的过程,但它可以包含在库中。不,对不起,我没有这样的图书馆。

答案 2 :(得分:2)

很难说出你在这里问的是什么。您当然可以选择特定类型T,将ByteString反序列化,然后将其存储在AnyNode中。尽管如此,这并不能使AnyNode的用户感觉良好 - 毕竟你还是选择了T。如果它不是Typeable约束,那么用户甚至不能告诉类型是什么(所以让我们摆脱Typeable约束,因为它会使事情变得更加混乱)。也许你想要的是普遍的而不是存在的。

让我们将Serialize分成两个类 - 将它们称为ReadShow - 并稍微简化一下(因此,例如read不会失败)。

所以我们有

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

我们可以为Show - 值创建一个存在容器:

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

但当然ShowExString是同构的,所以对此没有多少意义。但请注意,Read的存在主义更为重要:

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时 - 这意味着你有某种类型的值,而你不知道它是什么类型,但你可以{ {1}}转换为相同类型的另一个值。但你不能用它做任何事情! String只有生成 read s,但如果您不知道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基本上是ShowEx的参数 - 即show - 而show :: (∃a. Show a *> a) -> String基本上是ReadUn的返回值 - 即read

那么你要求什么,存在主义还是普遍存在?你当然可以制作read :: String -> (∀a. Read a => a)∀a. (Show a, Read a) => a之类的东西,但这里你也不是很好。真正的问题是量词。

(前一段时间我问a question我在另一个背景下讨论了其中一些内容。)