Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle generic deserialisation in haskell?

In my application I store and load objects from a database (currently a local flat file...). These objects are all from a type family Event a and are also all serialisable to/from ByteString. But the a in the type family can vary...

Here is the base class declaration:

class BusinessModel a where
  data Event a   :: *
  apply :: a -> Event a -> a

And an example implementation, along with needed types:

data User = User { userId :: UserId, userName :: Text } deriving (Eq, Show, Read, Generic)

instance ToJSON User
instance FromJSON User

type UsersView = M.Map UserId User

instance BusinessModel UsersView where

   data Event UsersView = NoEvent              -- ^A no-op event
                       | UserRegistered User
                       deriving (Eq, Show, Read, Generic)

  apply v (UserRegistered u)  = insert (userId u) u v
  apply v _                   = v

Here is the current interface to my event store:

class (MonadIO store) => EventStore store where
  -- store a single event
  store :: Serializable s => s -> store ()
  -- load all events from the store
  load  :: Serializable s => store [s]

Then we need to be able to serialise events (here we simply use the Json representation:

instance ToJSON (Event UsersView)
instance FromJSON (Event UsersView)

instance Serializable (Event UsersView) where
  read  = fromJust . decode
  write = encode

I would like to be able to deserialise all stored Event a objects, for all a, then apply each Event a to the correct a inside a structure that contains different events "target". When I try to naively use load I run into the trouble of having multiple instances of Serializable and not being able to chose the right one.

Out of the top of my head, I could think of a simple solution: Tag every event to be read with the proper a it pertains to, but this does not seem very elegant?

What is the best approach to this kind of problem?

like image 470
insitu Avatar asked Oct 31 '22 16:10

insitu


1 Answers

There's really no convenient, automatic method in Haskell for what you are trying to do. Deserializing arbitrary values and using them in run-time polymorphic ways via interfaces is more of an object oriented pattern.

As you said yourself, for serialization and deserialization you need to tag the events somehow to store and restore the type information. E.g.

data TaggedEvent
    = EventUsersView (Event UsersView)
    | EventSomeOtherView (Event SomeOtherView)
    ...

To remove some manual boilerplate, the TaggedEvent type could be automatically generated using Template Haskell as is done for example for the events in acid-state.

like image 94
shang Avatar answered Nov 09 '22 10:11

shang