For the real answer to this question, attempt to implement a generic serialization syntax for Haskell that requires no work by the user to use. Not even declaring typeclasses. Not even requiring Typeable to be implemented. Just feed it a datatype, and even if the other end has a different version of the software installed (which can happen in a distributed system, after all), it'll all Just Work to some degree.
Erlang has that, because it has its datatypes and a defined serialization for them and there is nothing in the language that is not those datatypes. It also doesn't permit you to layer any type-level assertions about those types into a user type. In fact, Erlang basically has no concept of user types. (Records are syntax sugar around the built-in tuple.) Since the Erlang data types are so weak, an automated serialization can be built that requires no work to use. But that doesn't come without cost
If you don't start with that, you have to use some sort of introspection to examine data types, and you probably don't have a good story for what to do if two ends have totally different ideas about those datatypes, or how to upgrade running processes where you literally want to change the datatype without shutting the process down. The stronger your type system, the harder that gets. The easier it is, the weaker your type system must be. Erlang's capabilities don't come for free, they exist because they wrote their (non-)type system so that their data structures never have any guarantees in them that don't trivially travel across the network. This has its own issues; Erlang has just about the weakest types you can have without actually having arbitrary casts, and that has consequences too.
There are a handful of characteristics in a language, like its type system, that have radical impacts throughout the language and no amount of library jiggery-pokery can completely paper over. Another example not entirely relevant to cross-process concurrency (but not entirely irrelevant) is immutability; no amount of library work can turn a mutable language into an immutable language.
This comment resonates with me especially well today.
I've been attempting to work with GWT and the serialization issues are entertaining (did you implement IsSerializable? is there a no-arg constructor? Do all of the classes your class depend on have the same? Did you remember to recompile the GWT/JS code to update the serialization white list? etc.).
It makes me appreciate more playing with Erlang a while back and shooting data around being so simple.
Erlang has that, because it has its datatypes and a defined serialization for them and there is nothing in the language that is not those datatypes. It also doesn't permit you to layer any type-level assertions about those types into a user type. In fact, Erlang basically has no concept of user types. (Records are syntax sugar around the built-in tuple.) Since the Erlang data types are so weak, an automated serialization can be built that requires no work to use. But that doesn't come without cost
If you don't start with that, you have to use some sort of introspection to examine data types, and you probably don't have a good story for what to do if two ends have totally different ideas about those datatypes, or how to upgrade running processes where you literally want to change the datatype without shutting the process down. The stronger your type system, the harder that gets. The easier it is, the weaker your type system must be. Erlang's capabilities don't come for free, they exist because they wrote their (non-)type system so that their data structures never have any guarantees in them that don't trivially travel across the network. This has its own issues; Erlang has just about the weakest types you can have without actually having arbitrary casts, and that has consequences too.
There are a handful of characteristics in a language, like its type system, that have radical impacts throughout the language and no amount of library jiggery-pokery can completely paper over. Another example not entirely relevant to cross-process concurrency (but not entirely irrelevant) is immutability; no amount of library work can turn a mutable language into an immutable language.