Deserialization of an existential data type

I need to write an instance of Serialize for the following data type:

 data AnyNode = forall n . (Typeable n, Serialize n) => AnyNode n 

Serialization is not a problem, but I cannot implement deserialization, since the compiler is not able to resolve a specific instance of Serialize n , since n isolated from the outer area.

In 2006 there was a related discussion . Now I wonder if there was any solution or workaround today.

+4
source share
3 answers

You just mark the type when you serialize, and use the dictionary to undo the type when deserializing. Here are some pseudo codes that exclude error checking, etc .:

 serialAnyNode (AnyNode x) = serialize (typeOf n, serialize x) deserialAnyNode s = case deserialize s of (typ,bs) -> case typ of "String" -> AnyNode (deserialize bs :: String) "Int" -> AnyNode (deserialize bs :: Int) .... 

Note that you can deserialize a closed type universe using your function. With some extra work, you can also deserialize derived types such as tuples, maybs, and eithers.

But if I were to declare a completely new type of "Gotcha", receiving Typeable and Serialize , deserialAnyNode , of course, could not cope with it without an extension.

+9
source

You must have some sort of centralized “registry” of deserialization functions, so you can send the actual type (extracted from Typeable information). If all the types you want to deserialize are in the same module, this is pretty easy to configure. If they are in multiple modules, you need to have one module that has a mapping.

If your type collection is more dynamic and inaccessible at compile time, you can use the dynamic link to access deserializers. For each type you want to deserialize, you export a C call function with a name derived from Typeable information (you can use TH to create them). Then at run time, when you want to deserialize the type, generate the same name and use the dynamic linker to get the address of the function, and then the FFI wrapper to get the function of the Haskell call. This is a rather complicated process, but it can be wrapped in a library. No, sorry, I do not have such a library.

+6
source

It is hard to say what exactly you are asking here. You can select a specific type T , deserialize the ByteString and save it to AnyNode . This does not make the AnyNode user much good, though - you still chose T , after all. If it weren’t for the Typeable restriction, the user couldn’t even say what the type is (so let's get rid of the Typeable restriction because it makes things more messy). Perhaps what you want is universal, not existential.

Divide Serialize into two classes - name them Read and Show - and simplify them a bit (for example, Read cannot fail).

So we have

 class Show a where show :: a -> String class Read a where read :: String -> a 

We can create an existential container for the Show -able value:

 data ShowEx where ShowEx :: forall a. Show a => a -> ShowEx -- non-GADT: data ShowEx = forall a. Show a => ShowEx a 

But, of course, ShowEx is isomorphic to String , so that doesn't make much sense. But keep in mind that existential for Read has an even smaller point:

 data ReadEx where ReadEx :: forall a. Read a => a -> ReadEx -- non-GADT: data ReadEx = forall a. Read a => ReadEx a 

When I give you ReadEx - ie ∃a. Read a *> a ∃a. Read a *> a - this means that you have a value of some type, and you do not know what a type is, but you can String into another value of the same type. But you can’t do anything about it! Read only creates a s, but that does not help you if you do not know what a .

What you may need with Read will be the type that allows you to select the caller, i.e. universal. Sort of

 newtype ReadUn where ReadUn :: (forall a. Read a => a) -> ReadUn -- non-GADT: newtype ReadUn = ReadUn (forall a. Read a => a) 

(Like ReadEx , you can do ShowUn - ie ∀a. Show a => a - and that would be just as useless.)

Note that ShowEx is essentially a Show argument - that is, show :: (∃a. Show a *> a) -> String - and ReadUn is essentially the return value of Read - i.e. read :: String -> (∀a. Read a => a) .

So what do you ask for, existential or universal? You can, of course, do something like ∀a. (Show a, Read a) => a ∀a. (Show a, Read a) => a or ∃a. (Show a, Read a) *> a ∃a. (Show a, Read a) *> a , but you are not very good here either. The real problem is the quantifier.

(I asked a question a while ago, where I spoke about this in a different context.)

+2
source

Source: https://habr.com/ru/post/1495153/


All Articles