Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binary instance for an existential

Tags:

haskell

Given an existential data type, for example:

data Foo = forall a . (Typeable a, Binary a) => Foo a

I'd like to write instance Binary Foo. I can write the serialisation (serialise the TypeRep then serialise the value), but I can't figure out how to write the deserialisation. The basic problem is that given a TypeRep you need to map back to the type dictionary for that type - and I don't know if that can be done.

This question has been asked before on the haskell mailing list http://www.haskell.org/pipermail/haskell/2006-September/018522.html, but no answers were given.

like image 664
Neil Mitchell Avatar asked Nov 11 '11 23:11

Neil Mitchell


3 Answers

You need some way that each Binary instance can register itself (just as in your witness version). You can do this by bundling each instance declaration with an exported foreign symbol, where the symbol name is derived from the TypeRep. Then when you want to deserialize you get the name from the TypeRep and look up that symbol dynamically (with dlsym() or something similar). The value exported by the foreign export can, e.g., be the deserializer function.

It's crazy ugly, but it works.

like image 68
augustss Avatar answered Nov 09 '22 19:11

augustss


This can be solved in GHC 7.10 and onwards using the Static Pointers Language extension:

{-# LANGUAGE StaticPointers #-}
{-# LANGUAGE InstanceSigs #-}

data Foo = forall a . (StaticFoo a, Binary a, Show a) => Foo a

class StaticFoo a where
    staticFoo :: a -> StaticPtr (Get Foo)

instance StaticFoo String where
    staticFoo _ = static (Foo <$> (get :: Get String))

instance Binary Foo where
    put (Foo x) = do
        put $ staticKey $ staticFoo x
        put x

    get = do
        ptr <- get
        case unsafePerformIO (unsafeLookupStaticPtr ptr) of
            Just value -> deRefStaticPtr value :: Get Foo
            Nothing -> error "Binary Foo: unknown static pointer"

A full description of the solution can be found on this blog post, and a complete snippet here.

like image 25
Abhiroop Sarkar Avatar answered Nov 09 '22 18:11

Abhiroop Sarkar


If you could do that, you would also be able to implement:

isValidRead :: TypeRep -> String -> Bool

This would be a function that changes its behavior due to someone defining a new type! Not very pure-ish.. I think (and hope) that one can't implement this in Haskell..

like image 2
yairchu Avatar answered Nov 09 '22 18:11

yairchu