我需要Serialize
为以下数据类型编写一个实例:
data AnyNode = forall n . (Typeable n, Serialize n) => AnyNode n
序列化 this 没问题,但我无法实现反序列化,因为编译器无法解析 的特定实例Serialize n
,因为n
与外部作用域隔离。
2006年有过相关讨论。我现在想知道今天是否有任何解决方案或解决方法。
您只需在序列化时标记类型,并在反序列化时使用字典取消标记类型。这是一些省略错误检查等的伪代码:
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”派生Typeable
and Serialize
,deserialAnyNode
当然无法在没有扩展的情况下处理它。
您需要有某种集中的反序列化功能“注册表”,以便您可以根据实际类型(从Typeable
信息中提取)进行调度。如果您要反序列化的所有类型都在同一个模块中,那么设置起来非常容易。如果它们位于多个模块中,则需要一个具有映射的模块。
如果您的类型集合更加动态且在编译时不容易获得,您也许可以使用动态链接来访问反序列化程序。对于您要反序列化的每种类型,您导出一个 C 可调用函数,其名称源自Typeable
信息(您可以使用 TH 生成这些)。然后在运行时,当您想要反序列化一个类型时,生成相同的名称并使用动态链接器来获取函数的地址,然后使用 FFI 包装器来获取 Haskell 可调用函数。这是一个相当复杂的过程,但它可以包含在一个库中。不,对不起,我没有这样的图书馆。
很难准确地说出你在这里问的是什么。你当然可以选择一个特定的类型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
只产生 a
s,但是当你不知道是什么时,这对你没有任何好处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
的返回值。read
read :: String -> (∀a. Read a => a)
那么你要的是什么,存在主义的还是普遍的?你当然可以做类似∀a. (Show a, Read a) => a
or的东西∃a. (Show a, Read a) *> a
,但你在这里也不是很好。真正的问题是量词。
(我刚才问了一个问题,我在另一个上下文中谈到了其中的一些问题。)