我需要为以下数据类型编写Serialize
实例:
data AnyNode = forall n . (Typeable n, Serialize n) => AnyNode n
序列化这个没问题,但我无法实现反序列化,因为编译器无法解析Serialize n
的特定实例,因为n
与外部范围隔离。 / p>
有a related discussion in 2006。我现在想知道今天是否有任何解决方案或解决方法。
答案 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”派生Typeable
和Serialize
,deserialAnyNode
当然无法在没有扩展的情况下处理它。
答案 1 :(得分:6)
您需要具有反序列化函数的某种集中式“注册表”,以便您可以调度实际类型(从Typeable
信息中提取)。如果要反序列化的所有类型都在同一模块中,则很容易设置。如果它们在多个模块中,则需要有一个具有映射的模块。
如果您的类型集合更加动态且在编译时不易获得,您可以使用动态链接来访问反序列化器。对于要反序列化的每种类型,导出一个C可调用函数,其名称源自Typeable
信息(您可以使用TH生成这些函数)。然后在运行时,当您想要反序列化类型时,生成相同的名称并使用动态链接器来获取函数的地址,然后使用FFI包装器来获取Haskell可调用函数。这是一个相当复杂的过程,但它可以包含在库中。不,对不起,我没有这样的图书馆。
答案 2 :(得分: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
- 值创建一个存在容器:
data ShowEx where
ShowEx :: forall a. Show a => a -> ShowEx
-- non-GADT: data ShowEx = forall a. Show a => ShowEx a
但当然ShowEx
与String
是同构的,所以对此没有多少意义。但请注意,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我在另一个背景下讨论了其中一些内容。)