views:

426

answers:

5

One of the huge benefits in languages that have some sort of reflection/introspecition is that objects can be automatically constructed from a variety of sources.

For example in Java I can use the same objects for persisting to a db (with Hibernate) serializing to XML (with JAXB) or serializing to JSON (json-lib). You can do the same in Ruby and Python also usually following some simple rules for properties or annotations for Java.

Thus I don't need lots "Domain Transfer Objects". I can concentrate on the domain I am working in.

It seems in very strict FP like Haskell and Ocaml this is not possible. Particularly Haskell. The only thing I have seen is doing some sort of preprocessing or meta-programming (ocaml). Is it just accepted that you have to do all the transformations from the bottom upwards?

In other words you have to do lot of boring work to turn a data type in haskell into JSON/XML/DB Row object and back again into a data object.

+3  A: 

For what it's worth, I think the pre-processor solution found in OCaml (as exemplified by sexplib, binprot and json-wheel among others) is pretty great (and I think people do very similar things with Template Haskell). It's far more efficient than reflection, and can also be tuned to individual types in a natural way. If you don't like the auto-generated serializer for a given type foo, you can always just write your own, and it fits beautifully into the auto-generated serializers for types that include foo as a component.

The only downside is that you need to learn camlp4 to write one of these for yourself. But using them is quite easy, once you get your build-system set up to use the preprocessor. It's as simple as adding with sexp to the end of a type definition:

type t = { foo: int; bar: float }
with sexp

and now you have your serializer.

zrr
I'll have to take a closer look at sexplib thanks :)
Adam Gent
+1  A: 

My understanding is that the simplest way to serialize and deserialize in Haskell is to derive from Read and Show. This is simple and isn't fullfilling your requirements.

However there are HXT and Text.JSON which seem to provide what you need.

pmr
+6  A: 

I can't speak to OCaml, but I'd say that the main difficulty in Haskell is that deserialization requires knowing the type in advance--there's no universal way to mechanically deserialize from a format, figure out what the resulting value is, and go from there, as is possible in languages with unsound or dynamic type systems.

Setting aside the type issue, there are various approaches to serializing data in Haskell:

  • The built-in type classes Read/Show (de)serialize algebraic data types and most built-in types as strings. Well-behaved instances should generally be such that read . show is equivalent to id, and that the result of show can be parsed as Haskell source code constructing the serialized value.

  • Various serialization packages can be found on Hackage; typically these require that the type to be serialized be an instance of some type class, with the package providing instances for most built-in types. Sometimes they merely require an automatically derivable instance of the type-reifying, reflective metaprogramming Data class (the charming fully qualified name for which is Data.Data.Data), or provide Template Haskell code to auto-generate instances.

  • For truly unusual serialization formats--or to create your own package like the previously mentioned ones--one can reach for the biggest hammer available, sort of a "big brother" to Read and Show: parsing and pretty-printing. Numerous packages are available for both, and while it may sound intimidating at first, parsing and pretty-printing are in fact amazingly painless in Haskell.

A glance at Hackage indicates that serialization packages already exist for various formats, including binary data, JSON, YAML, and XML, though I've not used any of them so I can't personally attest to how well they work. Here's a non-exhaustive list to get you started:

  • binary: Performance-oriented serialization to lazy ByteStrings
  • cereal: Similar to binary, but a slightly different interface and uses strict ByteStrings
  • genericserialize: Serialization via built-in metaprogramming, output format is extensible, includes R5RS sexp output.
  • json: Lightweight serialization of JSON data
  • RJson: Serialization to JSON via built-in metaprogramming
  • hexpat-pickle: Combinators for serialization to XML, using the "hexpat" package
  • regular-xmlpickler: Serialization to XML of recursive data structures using the "regular" package

The only other problem is that, inevitably, not all types will be serializable--if nothing else, I suspect you're going to have a hard time serializing polymorphic types, existential types, and functions.

camccann
Thanks for all the information. I really wanted to give you the correct answer but I was looking for more "automatic" and seems you have to do preprocessing.
Adam Gent
@Adam Gent: Well, as far as I can tell, the packages using the built-in generic metaprogramming are nearly automatic, needing only instances of `Data` and/or `Typeable`, both of which can be automatically derived by GHC. All that's required is a `deriving (Data, Typeable)` clause added to any `data`/`newtype` definitions, the same way one often uses automatic derived instances for `Show`, `Eq`, etc.
camccann
Darn why can't I give two people the right answer! Yeah your examples look good.
Adam Gent
+2  A: 

The usual approach is to employ Data.Binary. This provides the basic serialisation capability. Binary instances for data types are easy to write and can easily be built out of smaller units.

If you want to generate the instances automatically then you can use Template Haskell. I don't know of any package to do this, but I wouldn't be surprised if one already exists.

Paul Johnson
+1  A: 

to do lot of boring work to turn a data type in haskell into JSON/XML/DB Row object and back again into a data object.

There are many ways to serialize and unserialize data types in Haskell. You can use for example,

as well as other common formants (protocol buffers, thrift, xml)

Each package often/usually comes with a macro or deriving mechanism to allow you to e.g. derive JSON. For Data.Binary for example, see this previous answer: http://stackoverflow.com/questions/2260475/erlangs-term-to-binary-in-haskell

The general answer is: we have many great packages for seralization in Haskell, and we tend to use the existing class 'deriving' infrastructure (with either genreics or template haskell macros to do the actual deriving).

Don Stewart