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?
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With