4

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

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

序列化 this 没问题,但我无法实现反序列化,因为编译器无法解析 的特定实例Serialize n,因为n与外部作用域隔离。

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 和 anys。

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

于 2013-08-04T03:29:33.537 回答
6

您需要有某种集中的反序列化功能“注册表”,以便您可以根据实际类型(从Typeable信息中提取)进行调度。如果您要反序列化的所有类型都在同一个模块中,那么设置起来非常容易。如果它们位于多个模块中,则需要一个具有映射的模块。

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

于 2013-08-04T12:09:00.197 回答
2

很难准确地说出你在这里问的是什么。你当然可以选择一个特定的类型T,反序列化ByteString它,然后将它存储在一个AnyNode. 不过,这对用户并没有多大好处——毕竟AnyNode你还是选择了 。T如果没有Typeable约束,用户甚至无法分辨类型是什么(所以让我们摆脱Typeable约束,因为它使事情变得更加混乱)。也许你想要的是普遍的而不是存在的。

让我们Serialize分成两个类——调用它们Read并且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,所以没有太多的意义。但请注意,is 的存在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- 这意味着你有一个某种类型的值,你不知道类型是什么,但你可以String进入另一个相同类型的值。但是你不能用它做任何事情!read产生 as,但是当你不知道是什么时,这对你没有任何好处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它本质上是 --ie show--的参数show :: (∃a. Show a *> a) -> String,本质上是--ieReadUn的返回值。readread :: String -> (∀a. Read a => a)

那么你要的是什么,存在主义的还是普遍的?你当然可以做类似∀a. (Show a, Read a) => aor的东西∃a. (Show a, Read a) *> a,但你在这里也不是很好。真正的问题是量词。

(我刚才问了一个问题,我在另一个上下文中谈到了其中的一些问题。)

于 2013-08-04T03:35:29.220 回答