Haskell中的可扩展序列化

时间:2013-08-16 05:02:44

标签: haskell serialization types deserialization

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

假设我有这种类型:

data Foo = { bar :: Int } deriving (Show, Read)

列表:

foos = [Foo 1, Foo 2]

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

hPutStrLn fileHand . ppShow $ foos

然后我可以将它序列化:

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

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

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

感谢。

4 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:4)

根据您的使用情况,您还可以使用Yesod中的persistent将数据保存在数据库中。引用:

  

Persistent遵循类型安全和简洁,声明性语法的指导原则。其他一些不错的功能是:

     
      
  • 数据库无关。 PostgreSQL,SQLite,MySQL和MongoDB都有一流的支持,并且正在开发实验性的CouchDB支持。
  •   
  • 由于本质上是非关系型的,我们同时能够支持更多的存储层,并且不会受到通过连接产生的一些性能瓶颈的限制。
  •   
  • 处理SQL数据库的一个主要障碍是对架构的更改。 Persistent可以自动执行数据库迁移。
  •   
在这些情况下,

持久会为您处理数据更改:

  

对于以下情况,它将自动更改架构:

     
      
  • 字段的数据类型已更改。但是,如果无法翻译数据,数据库可能会反对此修改。
  •   
  • 添加了一个字段。但是,如果该字段不为null,则不提供默认值(稍后我们将讨论默认值)并且数据库中已有数据,数据库不会允许这种情况发生。
  •   
  • 字段从非null转换为null。在相反的情况下,Persistent将根据数据库的批准尝试转换。
  •   
  • 添加了一个全新的实体。
  •   

答案 2 :(得分:3)

希望能够在仍然能够访问您的内容的同时更改数据布局几乎是 定义发明数据库管理系统的动机之一。您是否考虑过将数据放入简单的SQLite表中?对于你正在尝试做的事情可能有些过分,但它有一些优点:

  • 几乎肯定比基于文本的编码效率更高。
  • 您仍然可以从应用程序外部轻松阅读它(例如,检查是否保存了正确的内容)。
  • &#34;转换文件&#34;现在相当于一个简单的SQL查询。
  • 如果您避免使用*作为列选择器,那么旧代码仍然可以读取由较新版本的应用程序创建的内容。 (即,转发兼容性以及向后)。
  • 或者,您可以阅读编写一些简单的样板代码,该代码读取数据库架构并为尚不存在的任何列提供默认值。

我不知道这是否适合您的情况,但值得考虑。

答案 3 :(得分:2)

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

data Foo a = { bar :: Int, extra :: a } deriving (Show, Read)

然后使用a必须可序列化的约束来定义序列化实例:

instance (Serialize a) => Serialize (Foo a) where ...

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

编辑:糟糕,刚刚意识到你在谈论漂亮的印刷。等效的解决方案是定义一个类型类:

class PrettyPrint a where
    pp :: a -> String

instance PrettyPrint () where
    pp () = ""

instance (PrettyPrint a) => PrettyPrint (Foo a) where
    pp = ... -- You fill this in