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.
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With