Haskell中的可扩展序列化

Ell*_*ask 5 serialization haskell types deserialization

我正在尝试使用read和show进行序列化/反序列化(这本身不是问题),但在某种意义上可以扩展(但不能缩小)数据类型.

假设我有这种类型:

data Foo = { bar :: Int } deriving (Show, Read)
Run Code Online (Sandbox Code Playgroud)

列表:

foos = [Foo 1, Foo 2]
Run Code Online (Sandbox Code Playgroud)

我可以轻松地将其反序列化为一个文件:

hPutStrLn fileHand . ppShow $ foos
Run Code Online (Sandbox Code Playgroud)

然后我可以序列化它:

!str <- hGetContents fileHand
let foosFromFile = fromMaybe [] $ (readMaybe :: String -> Maybe [Foo]) str
Run Code Online (Sandbox Code Playgroud)

但是假设几个月后我想在Foo类型中添加一个'baz'字段.旧格式文件的直接序列化将不再适用于读取,我将需要转换文件(我真的不想要).

那么,是否有一个优雅的(没有在程序本身中放置明确的版本控制逻辑)解决方案仍然序列化文件中的数据,并用默认值填充缺少的字段?也许某些类型的技巧?

谢谢.

sha*_*ang 10

这可能不是你想要的,因为你想避免显式版本控制,但我仍然想指出safecopy哪个是版本化序列化的首选解决方案,至少使它有点无痛.

我不认为在支持添加任意数量的新字段时有任何方法可以使用默认ShowRead实例,但您当然可以Read手动编写自己的实例来处理丢失的记录字段.但是,我认为这比使用更加费力和容易出错safecopy.


Gab*_*lez 2

是的。只需添加一个多态字段:

data Foo a = { bar :: Int, extra :: a } deriving (Show, Read)
Run Code Online (Sandbox Code Playgroud)

然后定义一个序列化实例,其约束a必须是可序列化的:

instance (Serialize a) => Serialize (Foo a) where ...
Run Code Online (Sandbox Code Playgroud)

当您不使用额外字段时,只需将 a 插入()其中,因为()它可以轻松序列化(并且已经有一个Serialize实例)。

编辑:哎呀,刚刚意识到你在谈论漂亮的印刷。等效的解决方案是定义一个类型类,如下所示:

class PrettyPrint a where
    pp :: a -> String

instance PrettyPrint () where
    pp () = ""

instance (PrettyPrint a) => PrettyPrint (Foo a) where
    pp = ... -- You fill this in
Run Code Online (Sandbox Code Playgroud)