Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extensible serialization in Haskell

I'm trying to make a serialization/deserialization using read and show (which is not a problem per se), but extensible in a sense that the data type can be extended (but not shrunk).

Suppose I have this type:

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

And the list:

foos = [Foo 1, Foo 2]

I can easily deserialize it into a file:

hPutStrLn fileHand . ppShow $ foos

Then I can serialize it back:

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

But suppose that months later I want to add a 'baz' field into the Foo type. The direct serialization from the old-format file will no longer work with read, I will need to convert the file (which I don't really want).

So, is there an elegant (without putting an explicit versioning logic in the program itself) solution to still serialize the data from a file, and filling-in missing fields with default values? Maybe some types tricks?

Thanks.

like image 931
Ellqs Hask Avatar asked Aug 16 '13 05:08

Ellqs Hask


2 Answers

This might not be what you are looking for since you want to avoid explicit versioning but I'd still like to point out safecopy which is the go-to solution for versioned serialization and at least makes it somewhat painless.

I don't think there's any way to use the default Show and Read instances while supporting adding an arbitrary amount of new fields, but you can of course write your own Read instance by hand which handles the missing record fields. However, I think that's more laborious and error prone than just using safecopy.

like image 197
shang Avatar answered Oct 05 '22 04:10

shang


Depending on your use case, you could also use persistent from Yesod to persist your data in a database. Quoting:

Persistent follows the guiding principles of type safety and concise, declarative syntax. Some other nice features are:

  • Database-agnostic. There is first class support for PostgreSQL, SQLite, MySQL and MongoDB, with experimental CouchDB support in the works.
  • By being non-relational in nature, we simultaneously are able to support a wider number of storage layers and are not constrained by some of the performance bottlenecks incurred through joins.
  • A major source of frustration in dealing with SQL databases is changes to the schema. Persistent can automatically perform database migrations.

Persistent handles changes in your data for you in those cases:

For the following cases, it will automatically alter the schema:

  • The datatype of a field changed. However, the database may object to this modification if the data cannot be translated.
  • A field was added. However, if the field is not null, no default value is supplied (we’ll discuss defaults later) and there is already data in the database, the database will not allow this to happen.
  • A field is converted from not null to null. In the opposite case, Persistent will attempt the conversion, contingent upon the database’s approval.
  • A brand new entity is added.
like image 25
Petr Avatar answered Oct 05 '22 04:10

Petr